goku
04/29/2024, 6:23 AMbase
repo with some shared types, and multiple repos for feature modules. Feature modules will depend on shared types, but they will also independently download the schema and generate types they are using. If I publish metadata and depend on it in my featureModule I’m getting error because schemaFiles is defined upstream, and this makes sense since only 1 module can have the schema.mbonnin
04/29/2024, 6:41 AMalwaysGenerateTyoesMatching.set(listOf(".*))
goku
05/01/2024, 4:10 AMapolloMetadata("com…")
syntax.
I saw this and ran the task but -apollo
doesn’t get published even if the task succeeds. Am I on the right path or do you have any thoughts?goku
05/01/2024, 5:57 AMgroup = "com.jvm"
at the root level so all publications are published to com.jvm
. In my project I am individually setting groupID for each publication like below:
create<MavenPublication>("schema") {
groupId = "com.test"
artifactId = "schema"
}
With this setup, my models are published to com.test
, but Apollo metadata defaults groupId to root.project.name
mbonnin
05/01/2024, 7:48 AMroot.project.name
)?goku
05/01/2024, 7:51 AMmbonnin
05/01/2024, 7:51 AMmbonnin
05/01/2024, 7:52 AMmbonnin
05/01/2024, 7:52 AMmain
, the setup is slightly different than in the 3.x
branch, you have to create the publication yourself:
create<MavenPublication>("apollo") {
from(components["apollo"])
artifactId = "jvm-producer-apollo"
}
mbonnin
05/01/2024, 7:53 AMmbonnin
05/01/2024, 7:53 AMgoku
05/01/2024, 7:55 AMmbonnin
05/01/2024, 7:58 AMalwaysGenerateTypesMatching.set(listOf(".*"))
goku
05/01/2024, 8:07 AMgoku
05/03/2024, 12:26 AM// schema.module with [alwaysGenerateTypesMatching=Context]
packageName = com.schema
generated type = com.schema.Context
//feature module
packageName = com.feature
generated Query type = com.feature.QueryA
generated X type = com.schema.X -> Shouldn't this be com.feature.X?
I understand this will result in same class being duplicated under different namespaces but they would be fully decoupled the way fragments/queries are.
I guess another downside of this approach is that it will require mapping between two types if they are used in a parent module.mbonnin
05/03/2024, 8:25 AMI guess another downside of this approach is that it will require mapping between two types if they are used in a parent module.
Exactly! There are 2 kind of symbols generated: • executable (fragment/queries) in the
com.feature
package name
• schema types in the com.schema
package
If we generates the schema type in the com.featureN
module it means you can't share them anymoregoku
05/07/2024, 5:23 AMbuttonFragment
, and both modules have different package signature.
com.feature.A.fragment.ButtonFragment
and
com.feature.B.fragment.ButtonFragment
However, I’m getting Duplicate Type error. Their namespaces are different, shouldn’t this pass the checks?
Looking at the task, it is looking at the entry key, not the name space. I think this is nice-to-have, to encourage devs to share fragments to avoid duplication but it shouldn’t be mandatory imo. Idk how this works in v4mbonnin
05/07/2024, 3:20 PMmbonnin
05/07/2024, 3:23 PMmbonnin
05/07/2024, 3:23 PMgoku
05/08/2024, 1:00 AMgoku
05/10/2024, 5:54 AMservice("old") {}
service("new") {}
// dependencies
apolloMetadata("com.new-service-metadata")
When I add apolloMetadata(…)
, the legacy block (“old”) is complaining because the metadata doesn’t contain a service called “old”.
Is there a way to tell the plugin to use the apolloMetadata
for the new
service only not both?mbonnin
05/10/2024, 7:53 AMdependencies {
add("apolloNewSchemaConsumer", project(":schema"))
add("apolloNewConsumer", project(":schema"))
}
mbonnin
05/10/2024, 7:54 AMgoku
05/13/2024, 2:01 AMgoku
05/20/2024, 12:52 AMusedCoordinates
. If I add a downstream module using usedCoordinates
, does that mean all the types used in that downstream module will be generated in the upstream module? My assumption was that, it would only generate conflicting types in the upstream module. What I’m observing is all the used types of the module are pushed to parent module (which is better for my use case, but want to verify my understanding is correct).bod
05/20/2024, 7:38 AMalwaysGenerateTypesMatching()
with all the types of the referenced module).goku
05/26/2024, 9:03 AMmbonnin
05/27/2024, 10:25 AMgoku
06/06/2024, 3:49 AM- shared-types
- account
- profile
- transactions
On client side;
• create a schema module that generate types for shared-types
subgraph
• account
feature module would depend on the schema module and it also downloads account
subgraph and generates types from that subgraph, with a namespace unique to this module.
• profile
and transactions
modules would do the same etc.
• if account
module needs to use types from profile
, then they download account
subgraph + profile
subgraph.
This will probably be impossible to implement today, because of the restriction of one schema module (unless we find a hacky way), but want to hear your thoughts to hear if in the future this could be an option.mbonnin
06/10/2024, 9:58 AMmbonnin
06/10/2024, 9:59 AMmbonnin
06/10/2024, 10:47 AMgoku
06/11/2024, 1:09 AMaccount
module’s subgraph has a breaking change, and profile
module updates the client schema to the latest profile
team has to go and fix breaking changes from account
module. I might be using the wrong term but Microfrontend ARchitecture is the closest thing comes to mind to explain this. Vertical ownership and reduced impact from changes in other subgraphs.mbonnin
06/11/2024, 7:29 AMmbonnin
06/11/2024, 7:30 AM# before
input UserInput {
name: String
email: String
}
# after
input UserInput {
email: String
name: String
}
mbonnin
06/11/2024, 7:31 AM// Before
Input.Builder()
.name("John Doe")
.email("<mailto:john@doe.com|john@doe.com>")
.build()
// After (same code works)
Input.Builder()
.name("John Doe")
.email("<mailto:john@doe.com|john@doe.com>")
.build()
mbonnin
06/11/2024, 7:33 AMgoku
06/12/2024, 5:53 AMgoku
06/27/2024, 6:20 AMusedCoordinates
and apolloSchema
in v3
TL;DR,
How do I consume Schema from another repository similar to how we are consuming Metadata?
Longer explanation…
Here is my setup:
:schema-module
publishes:
- generated types (some of them, not all)
- metadata
In another repository
:common-models:
\ :featureA-models:
\ :featureB-models:
I want to use usedCoordinates
in the second repository so duplicate types from featureA
and featureB
can be generated inside common-models
.
I believe I need two things:
1. Add all modules to common-models
with usedCoordinates
2. Add apolloSchema
to feature modules.
No problem with the 1st step but stuck in the second step.
• Tried adding apolloSchema(schema-module-metadata)
to feature modules but not luck. I saw that the json file has schema
in it so I thought this would work but after checking .module
file I can see there isn’t apollo-schema
variant.
• Tried adding apolloSchema(schema-module-metadata)
to common-models
and apolloSchema(common-models)
to feature modules but no luck either.
• I checked the generated schema.graphqls
file in common-models
but it is empty: Could not find a schema, make sure to add dependencies to the 'apolloSchema' configuration
• And I cannot provide a local one since it says “upstream module already provided a schema”
:common-models:
usedCoordinates(:featureA-models)
usedCoordinates(:featureB-models)
:featureA-models:
apolloSchema(?)
:featureB-models:
apolloSchema(?)
P.S. I can always use alwaysGenerateTypes
property in common-models
but I’m avoiding it intentionally so we don’t have thousands of classes for auto complete etc.mbonnin
06/27/2024, 7:34 AMusedCoordinates
doesn’t work cross repositories. That’s because there’s a “somewhat” circular dependency between modules when using usedCoordinates
. It works in the same repo because it’s not a true circular dependency in terms on task dependencies but it is a true circular dependency in terms of modules.mbonnin
06/27/2024, 7:38 AMfeatureA-models
2. consume that iR in common-models
and publish metadata there
3. consume that metadata in featureA-models
again
So it’s kind of a weird setupmbonnin
06/27/2024, 8:47 AM.graphql
files need to live in a separate module from the Kotlin code, this is a line I’m not really ready to cross (but maybe we need?):
:common-schema:
produces common-apolloSchema
:featureA-graphql:
consumes common-apolloSchema
produces featureA-apolloIr
:common-types:
consumes featureA-apolloIr
produces common-apolloMetadata
produces com.example.schema.*.kt
:featureA-kotlin:
consumes common-apolloSchema
consumes common-apolloMetadata
consumes featureA-apolloIr
produces com.example.operation.*.kt
mbonnin
06/27/2024, 8:48 AMgoku
06/27/2024, 10:51 AMIf you wanted to get the used coordinates across repositoriesI’m confused about this one. Isn’t it still within the same repository? The types in
schema-module
are already generated and published to jfrog. In the 2nd repository, instead of generating types in the feature modules I want to delegate type generation to common-models
schema using usedCoordinates
.
Here’s the part I am not understanding. In the entire setup we can have only 1 schema module, and we do. But it is not clear how it is imported to other repository. If I place a local copy of the schema and set it using schemaFile
I get duplicate schema error. Which means the schema from metadata.json
is recognized. But, when I use apolloSchema
that schema is ignored?
Until we get to v4 I will probably keep everything in the same module or use alwaysGenerateTypes
option, but beyond solving my problem I’m curious to understand how the schema from the metadata file is used.mbonnin
06/27/2024, 11:49 AMgoku
06/27/2024, 11:50 AMmbonnin
06/27/2024, 11:50 AMWhich means the schema fromLet me check,is recognized. But, when I usemetadata.json
that schema is ignored?apolloSchema
metadata.json
should indeed contain the schemagoku
06/27/2024, 11:52 AMschema
and sdl
fields. But in the generated folder schema.graphqls
file is still empty.mbonnin
06/27/2024, 12:02 PM-apollo
to the maven artifact id. I’m not 100% sure why TBHmbonnin
06/27/2024, 12:04 PMmbonnin
06/27/2024, 12:04 PMgoku
06/27/2024, 12:23 PMapolloSchema
configuration.
I can add apolloMetadata("com.example:schema-apollo:0.0.1")
to my common-models
which is in a different repository. No problem with this.
Then I add usedCoordinates(featureA)
and usedCoordinates(featureB)
to common-models
. No build error so far, but usedCoordinates
still not working since there is no schema specified in featureA
and featureB
.
This is where I get stuck. I need to be able to do in feature modules:
*apolloSchema*("com.example:schema-apollo:0.0.1")
mbonnin
06/27/2024, 12:26 PMmbonnin
06/27/2024, 12:27 PMapolloSchema
per say, it’s all in apolloMetadata
mbonnin
06/27/2024, 12:29 PMusedCoordinates
, it’s all a big spaghetti mealmbonnin
06/27/2024, 12:30 PMmbonnin
06/27/2024, 12:35 PM:schema-module:apollo-schema:0.0.1
:feature:ir:0.0.1
:schema-module:metadata:0.0.1
:feature:metadata:0.0.1
goku
06/27/2024, 12:36 PMmbonnin
06/27/2024, 12:36 PMapollo-schema
in common
2. release apollo-ir
in feature
3. go back to common and now release apollo-metadata
4. go brack to feature and release the rest of itmbonnin
06/27/2024, 12:37 PMmbonnin
06/27/2024, 12:37 PMmbonnin
06/27/2024, 12:37 PMmbonnin
06/27/2024, 12:38 PMgoku
06/27/2024, 12:40 PMcommon
. We only publish schema-module
Anything inside the second repo is where the apps live, it is the leaf node.
Is it difficult create schema.graphqls
file using the schema
from metadata.json
file? Wouldn’t this solve the problem?mbonnin
06/27/2024, 12:41 PMBtw we dont release the schema fromYup sorry, I meant. We only publishcommon
schema-module
schema-module
instead of common
mbonnin
06/27/2024, 12:43 PMIs it difficult createSomewhat?file using theschema.graphqls
fromschema
file? Wouldn’t this solve the problem?metadata.json
mbonnin
06/27/2024, 12:43 PMmbonnin
06/27/2024, 12:44 PM:feature:ir
to generate schema-module:metadata
mbonnin
06/27/2024, 12:45 PM:schema-module:schema:0.0.1
:feature:ir:0.0.1
:schema-module:metadata:0.0.1
:feature:metadata:0.0.1
mbonnin
06/27/2024, 12:46 PMschema
is the GraphQL schema + enforced invariants (scalar mappings, maybe something else, I’d need to double check)
• ir
is the GraphQL operations validated and transformed to an intermediate representation. This is where usedCoordinates are computed from
• metadata
is the list of generated Kotlin models and their matching GraphQL identifier so that downstream codegen can reference themmbonnin
06/27/2024, 12:47 PMmetadata
is the part that actually runs kotlinpoet
goku
06/27/2024, 12:48 PMYou needMaybe I’m missing something. Butto generate:feature:ir
schema-module:metadata
schema-module:metadata
is already generated in a separate repository and shipped.mbonnin
06/27/2024, 12:48 PMButThen it’s generated without the downstream typesis already generated in a separate repository and shipped.schema-module:metadata
mbonnin
06/27/2024, 12:48 PMmetadata
, there’s a list of ResolverEntry
mbonnin
06/27/2024, 12:49 PMResolverEntry
is a pair GraphQL name, Kotlin name. This is so that downstream codegen knows that if they reference a GraphQL type like SomeType
, they can use fully qualified name com.example.type.SomeType.kt
goku
06/27/2024, 12:50 PMschema-module
has some types. (too much context here but in short it is an internal library we’ve been using in a monorepo. now we are splitting to multiple repos so we are reusing this module.)
In downstream, feature modules will generate missing types that don’t exist in schema-module
. However, this is problematic when you have multiple feature modules in a single repo. So I want to generate rest of the missing types in common-models
.mbonnin
06/27/2024, 12:51 PMHowever, this is problematic when you have multiple feature modules in a single repo.Can you ellaborate what is problematic? Multiple redefinitions?
mbonnin
06/27/2024, 12:51 PMcommon-models
mbonnin
06/27/2024, 12:52 PMgoku
06/27/2024, 12:52 PMfeatureA
and featureB
has TypeC
we get duplicate type errors so we want to push this type to common-models
.mbonnin
06/27/2024, 12:54 PMmbonnin
06/27/2024, 12:55 PMgoku
06/27/2024, 12:57 PMmbonnin
06/27/2024, 12:58 PMmbonnin
06/27/2024, 12:58 PMmbonnin
06/27/2024, 1:01 PM:schema-module:schema:0.0.1
:featureA:ir:0.0.1
:featureB:ir:0.0.1
:common-models:ir:0.0.1
:schema-module:ir:0.0.1
:schema-module:metadata:0.0.1
:common-models:metadata:0.0.1
:featureA:metadata:0.0.1
:featureB:metadata:0.0.1
goku
06/27/2024, 1:01 PMmbonnin
06/27/2024, 1:02 PMmbonnin
06/27/2024, 1:03 PMgoku
06/27/2024, 1:04 PMmbonnin
06/27/2024, 1:06 PMmbonnin
06/27/2024, 1:06 PMcommon-models
would become the root as far as the Apollo Gradle Plugin hierarchy is concernedmbonnin
06/27/2024, 1:09 PMgoku
06/27/2024, 1:09 PMcommon-models
for now. I was planning of doing that at the schema-module
anyway. Major concern was having too many classes for auto complete and such but that should be okmbonnin
06/27/2024, 1:11 PMmbonnin
06/27/2024, 1:13 PMpackageName
.goku
06/27/2024, 1:13 PMmbonnin
06/27/2024, 1:14 PMmbonnin
06/27/2024, 1:15 PMmbonnin
06/27/2024, 1:16 PMin the schema repo there will always be some shared components.and
generating full types inSo you’re duplicating some types here? Maybe with different package names?for nowcommon-models
goku
06/27/2024, 1:17 PMcommon-models
depends on schema-module-apollo
, so it will only generate what is already not in schema-module
mbonnin
06/27/2024, 1:17 PMgoku
06/27/2024, 1:19 PMmbonnin
06/27/2024, 1:19 PMschema-module
has a single descendant in each binary. This is the important partmbonnin
06/27/2024, 1:20 PMschema-module
is published independantlygoku
06/27/2024, 1:20 PMmbonnin
06/27/2024, 1:21 PMgoku
06/27/2024, 1:23 PMmbonnin
06/27/2024, 1:24 PMgoku
06/27/2024, 1:26 PMmbonnin
06/27/2024, 2:55 PMmbonnin
06/27/2024, 2:56 PMmbonnin
06/27/2024, 3:08 PMcommon-models
repo and then let that be propagated downstream (commit)mbonnin
06/27/2024, 3:21 PMusedCoordinates
is supplied so it’s not that different from v4mbonnin
06/27/2024, 3:22 PMapp1/common-models
usedCoordinates from common-schema
)mbonnin
06/27/2024, 3:29 PMlistOf(".*")
)
If we revisit that, I think it’d make sense to only generate the “conflicts” at a given node, i.e. selfUsedCoordinates + conflictingDownStreamCoordinates
Please open an issue and we’ll look into it.goku
06/27/2024, 10:09 PMmbonnin
06/27/2024, 10:28 PMwe wont be able to avoid generating full schema, but I will test it out first.Thanks! Let us know how that goes! I’m not opposed to introducing more complex options there but I’d like to quantify the wins and make a clear use case before doing so.
is there a way to debug the new plugin?Summoning @bod for IntelliJ plugin debugging.
bod
06/28/2024, 8:14 AMBtw, is there a way to debug the new plugin? I’m running operationBased migration action, it shows that it is going through files but it gets stuck. It doesn’t say it gets stuck but overnight it’s been running.Wow sorry to hear that 😞 So you see the dialog with the progress bar that gets stuck in the middle, is that it? The first thing is to check if there has been a crash in the plugin:
Help
| Show Log in Finder
and see if you notice any stacktrace in idea.log
.
If not - there aren't a lot of logs in that area of the plugin I'm afraid. So the only way to debug would be to clone the repo and run the plugin from the IDE and add breakpoints 😅. It's a bit much but if you're motivated I can assist.bod
06/28/2024, 8:16 AMgoku
07/01/2024, 6:13 AMbod
07/01/2024, 6:42 AMgoku
07/03/2024, 5:42 AMgoku
07/03/2024, 6:05 AMSEVERE - #c.i.p.i.s.PsiSearchHelperImpl - Error during processing of: SomeViewModel.kt
org.jetbrains.kotlin.utils.KotlinExceptionWithAttachments: Unsupported reference
at org.jetbrains.kotlin.idea.search.usagesSearch.ExpressionsOfTypeProcessor$addClassToProcess$ProcessClassUsagesTask$perform$3.invoke(ExpressionsOfTypeProcessor.kt:245)
at org.jetbrains.kotlin.idea.search.usagesSearch.ExpressionsOfTypeProcessor$addClassToProcess$ProcessClassUsagesTask$perform$3.invoke(ExpressionsOfTypeProcessor.kt:215)
at org.jetbrains.kotlin.idea.search.usagesSearch.ExpressionsOfTypeProcessor$searchReferences$1$1.invoke(ExpressionsOfTypeProcessor.kt:951)
at org.jetbrains.kotlin.idea.search.usagesSearch.ExpressionsOfTypeProcessor$searchReferences$1$1.invoke(ExpressionsOfTypeProcessor.kt:949)
at com.intellij.openapi.application.ActionsKt.runReadAction$lambda$3(actions.kt:31)
...
at com.apollographql.ijplugin.refactoring.RefactoringKt.findReferences(Refactoring.kt:48)
at com.apollographql.ijplugin.refactoring.migration.compattooperationbased.item.ReworkInlineFragmentFields.findUsages(ReworkInlineFragmentFields.kt:27)
at com.apollographql.ijplugin.refactoring.migration.ApolloMigrationRefactoringProcessor.findUsages(ApolloMigrationRefactoringProcessor.kt:86)
...
SEVERE - #c.i.p.i.s.PsiSearchHelperImpl - Plugin to blame: Apollo GraphQL version: 4.0.0-beta.7
SEVERE - #c.i.p.i.s.PsiSearchHelperImpl - Last Action: CompatToOperationBasedCodegenMigrationAction
goku
07/03/2024, 6:33 AMfindReferences(Refactoring.kt:48)
• Couple of them are caused by Kotlin:
2024-07-03 14:14:26,726 [187287894] SEVERE - #c.i.p.i.s.PsiSearchHelperImpl - Plugin to blame: Kotlin version: 233.14808.21.2331.11842104-AS
2024-07-03 14:14:26,726 [187287894] SEVERE - #c.i.p.i.s.PsiSearchHelperImpl - Last Action: CompatToOperationBasedCodegenMigrationAction
• Other one is an exception with no details:
java.lang.IllegalStateException: root
at org.jetbrains.kotlin.name.FqNameUnsafe.shortName(FqNameUnsafe.java:138)
at org.jetbrains.kotlin.name.FqName.shortName(FqName.java:88)
at com.apollographql.ijplugin.navigation.GraphQLNavigationKt.isApolloOperationOrFragment(GraphQLNavigation.kt:92)
at com.apollographql.ijplugin.navigation.GraphQLNavigationKt.isApolloOperationOrFragmentReference(GraphQLNavigation.kt:54)
at com.apollographql.ijplugin.navigation.KotlinDefinitionMarkerProvider.collectNavigationMarkers(KotlinDefinitionMarkerProvider.kt:35)
For the first one, I checked the fragment, it is an inline fragment on an interface. Something like this with multiple layers; a field inside Square
would also have interface/implementation
Maybe the plugin is missing a use case here
interface Shape
type Square: Shape
type Circle: Shape
fragment squareOrCircle on Shape {
... on Square {
...
}
... on Circle {
...
}
}
bod
07/03/2024, 10:14 AMApollo
in theregoku
07/09/2024, 1:40 AMbod
07/09/2024, 9:37 AMUnsupported reference
crash while searching. The ‘good news’ is that the latest version of the plugin (4.0.0-rc.1) is resilient and will not crash. The bad news is that this may render the refactoring useless - it won’t find the necessary references, and your code won’t be migrated (would still be interesting to try the RC version and see what happens). It’s hard to tell what triggers it. Looks like it can happen if a references is found in something that’s neither Kotlin, Java, Groovy, Scala or Clojure… If that rings a bell 😅goku
07/09/2024, 9:45 AMbod
07/09/2024, 10:04 AMgoku
07/10/2024, 3:37 AMUnsupported reference
one is for inline fragments but I don’t see anything that stands out.
Is it complicated to pull in the source code and run this locally maybe I can debug?
Update: I can build it locally and see the output in build/distributions
folders but is there a way to hot load it to the IDE without going Plugins -> Install and restarting the IDE?
And no Scala or anything. It is just an Android ViewModel classgoku
07/10/2024, 5:38 AMbod
07/10/2024, 7:43 AM~/.gradle/gradle.properties
file:
apolloIntellijPlugin.ideDir=/Applications/Android Studio <http://Stable.app/Contents|Stable.app/Contents>
(assuming that's the right path to Android Studio on your machine / also assuming MacOS)
One little annoyance: AS will say the GraphQL plugin must be updated - you'll need to do that, quit, and run again.goku
07/11/2024, 3:21 AMstudio.vmoptions
but I don’t think it is using that file at all
I tried this but no luck.
runIde {
maxHeapSize = "8g"
}
goku
07/11/2024, 4:40 AMtypealias
.
All 3 files that failed had typealias
in them.
The YouTrack issue you commented on, and linked tickets also had typealias
in them.
Is it possible to ignore those files and continue processing?goku
07/11/2024, 5:08 AMbod
07/11/2024, 7:55 AMbod
07/11/2024, 9:38 AM"Unsupported reference"
exception - but it looks like it's correctly caught by the IDE in my case (doesn't reach the plugin's try/catch, and the reference is correctly returned and handled). Was wondering: which version of AS are you running? Also maybe can you share what kind of typealiasing you have in your project, maybe my repro case is a bit different.goku
07/11/2024, 9:47 AMbod
07/11/2024, 9:51 AMgoku
07/11/2024, 1:00 PMbod
07/11/2024, 1:02 PMgoku
07/11/2024, 1:05 PMbod
07/11/2024, 1:06 PMgoku
07/11/2024, 1:08 PMrunIde
settings, no luck.
But Ui freeze was also happening when I tested the RC build, which had 8g memorybod
07/11/2024, 1:14 PMgoku
07/11/2024, 2:06 PMbod
07/11/2024, 2:08 PMgoku
07/15/2024, 12:55 AMmoduleScope
. This works well for a few modules but when you loop through all modules (320 modules including test/unittest) you run into same issue.
We will either do this migration manually, or I will split the modules to 5-6 groups and run the migration separately.bod
07/15/2024, 6:30 AM