From c482ce3f26ce2f3b6c3317694bf42650a4663583 Mon Sep 17 00:00:00 2001 From: Nicholas Yang Date: Tue, 17 Sep 2024 17:06:49 -0400 Subject: [PATCH] feat(query): add array type (#9161) ### Description Added an `Array` type that automatically adds a `length` and `items` field to GraphQL responses. ### Testing Instructions --- crates/turborepo-lib/src/query.rs | 32 ++- turborepo-tests/integration/package.json | 2 +- turborepo-tests/integration/tests/affected.t | 132 +++++++------ .../integration/tests/command-query.t | 186 ++++++++++-------- 4 files changed, 204 insertions(+), 148 deletions(-) diff --git a/crates/turborepo-lib/src/query.rs b/crates/turborepo-lib/src/query.rs index b939b898663c5..c1ce6a1314399 100644 --- a/crates/turborepo-lib/src/query.rs +++ b/crates/turborepo-lib/src/query.rs @@ -38,6 +38,20 @@ impl Query { } } +#[derive(Debug, SimpleObject)] +struct Array { + items: Vec, + length: usize, +} + +impl FromIterator for Array { + fn from_iter>(iter: I) -> Self { + let items: Vec<_> = iter.into_iter().collect(); + let length = items.len(); + Self { items, length } + } +} + struct Package { run: Arc, name: PackageName, @@ -293,7 +307,7 @@ impl Query { &self, base: Option, head: Option, - ) -> Result, Error> { + ) -> Result, Error> { let mut opts = self.run.opts().clone(); opts.scope_opts.affected_range = Some((base, head)); @@ -322,7 +336,7 @@ impl Query { } /// Gets a list of packages that match the given filter - async fn packages(&self, filter: Option) -> Result, Error> { + async fn packages(&self, filter: Option) -> Result, Error> { let Some(filter) = filter else { return Ok(self .run @@ -369,7 +383,7 @@ impl Package { } /// The upstream packages that have this package as a direct dependency - async fn direct_dependents(&self) -> Result, Error> { + async fn direct_dependents(&self) -> Result, Error> { let node: PackageNode = PackageNode::Workspace(self.name.clone()); Ok(self .run @@ -386,7 +400,7 @@ impl Package { } /// The downstream packages that directly depend on this package - async fn direct_dependencies(&self) -> Result, Error> { + async fn direct_dependencies(&self) -> Result, Error> { let node: PackageNode = PackageNode::Workspace(self.name.clone()); Ok(self .run @@ -402,7 +416,8 @@ impl Package { .collect()) } - async fn all_dependents(&self) -> Result, Error> { + /// The downstream packages that depend on this package, transitively + async fn all_dependents(&self) -> Result, Error> { let node: PackageNode = PackageNode::Workspace(self.name.clone()); Ok(self .run @@ -417,7 +432,8 @@ impl Package { .collect()) } - async fn all_dependencies(&self) -> Result, Error> { + /// The upstream packages that this package depends on, transitively + async fn all_dependencies(&self) -> Result, Error> { let node: PackageNode = PackageNode::Workspace(self.name.clone()); Ok(self .run @@ -433,7 +449,7 @@ impl Package { } /// The downstream packages that depend on this package, indirectly - async fn indirect_dependents(&self) -> Result, Error> { + async fn indirect_dependents(&self) -> Result, Error> { let node: PackageNode = PackageNode::Workspace(self.name.clone()); let immediate_dependents = self .run @@ -456,7 +472,7 @@ impl Package { } /// The upstream packages that this package depends on, indirectly - async fn indirect_dependencies(&self) -> Result, Error> { + async fn indirect_dependencies(&self) -> Result, Error> { let node: PackageNode = PackageNode::Workspace(self.name.clone()); let immediate_dependencies = self .run diff --git a/turborepo-tests/integration/package.json b/turborepo-tests/integration/package.json index 55edcdeb477ec..252724245b068 100644 --- a/turborepo-tests/integration/package.json +++ b/turborepo-tests/integration/package.json @@ -2,7 +2,7 @@ "name": "turborepo-tests-integration", "scripts": { "test": "prysk tests", - "test:interactive": "prysk --interactive tests", + "test:interactive": "PRYSK_INTERACTIVE=true prysk tests", "test:parallel": ".cram_env/bin/pytest -n auto tests --prysk-shell=`which bash`", "pretest:parallel": ".cram_env/bin/pip3 install --quiet pytest \"prysk[pytest-plugin]\" pytest-xdist" }, diff --git a/turborepo-tests/integration/tests/affected.t b/turborepo-tests/integration/tests/affected.t index 8828d73ddbcac..b35b50b173031 100644 --- a/turborepo-tests/integration/tests/affected.t +++ b/turborepo-tests/integration/tests/affected.t @@ -42,15 +42,17 @@ Do the same thing with the `ls` command Do the same thing with the `query` command - $ ${TURBO} query "query { affectedPackages { name } }" + $ ${TURBO} query "query { affectedPackages { items { name } } }" WARNING query command is experimental and may change in the future { "data": { - "affectedPackages": [ - { - "name": "my-app" - } - ] + "affectedPackages": { + "items": [ + { + "name": "my-app" + } + ] + } } } @@ -88,15 +90,17 @@ Do the same thing with the `ls` command Do the same thing with the `query` command - $ ${TURBO} query "query { affectedPackages { name } }" + $ ${TURBO} query "query { affectedPackages { items { name } } }" WARNING query command is experimental and may change in the future { "data": { - "affectedPackages": [ - { - "name": "my-app" - } - ] + "affectedPackages": { + "items": [ + { + "name": "my-app" + } + ] + } } } @@ -130,15 +134,17 @@ Do the same thing with the `ls` command Do the same thing with the `query` command - $ ${TURBO} query "query { affectedPackages { name } }" + $ ${TURBO} query "query { affectedPackages { items { name } } }" WARNING query command is experimental and may change in the future { "data": { - "affectedPackages": [ - { - "name": "my-app" - } - ] + "affectedPackages": { + "items": [ + { + "name": "my-app" + } + ] + } } } @@ -163,11 +169,13 @@ Do the same thing with the `ls` command Do the same thing with the `query` command - $ ${TURBO} query "query { affectedPackages(base: \"HEAD\") { name } }" + $ ${TURBO} query "query { affectedPackages(base: \"HEAD\") { items { name } } }" WARNING query command is experimental and may change in the future { "data": { - "affectedPackages": [] + "affectedPackages": { + "items": [] + } } } @@ -192,11 +200,13 @@ Do the same thing with the `ls` command Do the same thing with the `query` command - $ ${TURBO} query "query { affectedPackages(head: \"main\") { name } }" + $ ${TURBO} query "query { affectedPackages(head: \"main\") { items { name } } }" WARNING query command is experimental and may change in the future { "data": { - "affectedPackages": [] + "affectedPackages": { + "items": [] + } } } @@ -235,15 +245,17 @@ Do the same thing with the `ls` command Do the same thing with the `query` command - $ ${TURBO} query "query { affectedPackages { name } }" + $ ${TURBO} query "query { affectedPackages { items { name } } }" WARNING query command is experimental and may change in the future { "data": { - "affectedPackages": [ - { - "name": "my-app" - } - ] + "affectedPackages": { + "items": [ + { + "name": "my-app" + } + ] + } } } @@ -277,26 +289,28 @@ Do the same thing with the `ls` command Do the same thing with the `query` command - $ ${TURBO} query "query { affectedPackages { name } }" + $ ${TURBO} query "query { affectedPackages { items { name } } }" WARNING query command is experimental and may change in the future WARNING unable to detect git range, assuming all files have changed: git error: fatal: no merge base found { "data": { - "affectedPackages": [ - { - "name": "//" - }, - { - "name": "another" - }, - { - "name": "my-app" - }, - { - "name": "util" - } - ] + "affectedPackages": { + "items": [ + { + "name": "//" + }, + { + "name": "another" + }, + { + "name": "my-app" + }, + { + "name": "util" + } + ] + } } } @@ -330,25 +344,27 @@ Do the same thing with the `ls` command Do the same thing with the `query` command - $ ${TURBO} query "query { affectedPackages { name } }" + $ ${TURBO} query "query { affectedPackages { items { name } } }" WARNING query command is experimental and may change in the future WARNING unable to detect git range, assuming all files have changed: git error: fatal: no merge base found { "data": { - "affectedPackages": [ - { - "name": "//" - }, - { - "name": "another" - }, - { - "name": "my-app" - }, - { - "name": "util" - } - ] + "affectedPackages": { + "items": [ + { + "name": "//" + }, + { + "name": "another" + }, + { + "name": "my-app" + }, + { + "name": "util" + } + ] + } } } \ No newline at end of file diff --git a/turborepo-tests/integration/tests/command-query.t b/turborepo-tests/integration/tests/command-query.t index 9d1842ee72488..0d924f0de5172 100644 --- a/turborepo-tests/integration/tests/command-query.t +++ b/turborepo-tests/integration/tests/command-query.t @@ -2,145 +2,169 @@ Setup $ . ${TESTDIR}/../../helpers/setup_integration_test.sh Query packages - $ ${TURBO} query "query { packages { name } }" | jq + $ ${TURBO} query "query { packages { items { name } } }" | jq WARNING query command is experimental and may change in the future { "data": { - "packages": [ - { - "name": "//" - }, - { - "name": "another" - }, - { - "name": "my-app" - }, - { - "name": "util" - } - ] + "packages": { + "items": [ + { + "name": "//" + }, + { + "name": "another" + }, + { + "name": "my-app" + }, + { + "name": "util" + } + ] + } } } Query packages with equals filter - $ ${TURBO} query "query { packages(filter: { equal: { field: NAME, value: \"my-app\" } }) { name } }" | jq + $ ${TURBO} query "query { packages(filter: { equal: { field: NAME, value: \"my-app\" } }) { items { name } } }" | jq WARNING query command is experimental and may change in the future { "data": { - "packages": [ - { - "name": "my-app" - } - ] + "packages": { + "items": [ + { + "name": "my-app" + } + ] + } } } Query packages that have at least one dependent package - $ ${TURBO} query "query { packages(filter: { greaterThan: { field: DIRECT_DEPENDENT_COUNT, value: 0 } }) { name } }" | jq + $ ${TURBO} query "query { packages(filter: { greaterThan: { field: DIRECT_DEPENDENT_COUNT, value: 0 } }) { items { name } } }" | jq WARNING query command is experimental and may change in the future { "data": { - "packages": [ - { - "name": "util" - } - ] + "packages": { + "items": [ + { + "name": "util" + } + ] + } } } Get dependents of `util` - $ ${TURBO} query "query { packages(filter: { equal: { field: NAME, value: \"util\" } }) { directDependents { name } } }" | jq + $ ${TURBO} query "query { packages(filter: { equal: { field: NAME, value: \"util\" } }) { items { directDependents { items { name } } } } }" | jq WARNING query command is experimental and may change in the future { "data": { - "packages": [ - { - "directDependents": [ - { - "name": "my-app" + "packages": { + "items": [ + { + "directDependents": { + "items": [ + { + "name": "my-app" + } + ] } - ] - } - ] + } + ] + } } } Get dependencies of `my-app` - $ ${TURBO} query "query { packages(filter: { equal: { field: NAME, value: \"my-app\" } }) { directDependencies { name } } }" | jq + $ ${TURBO} query "query { packages(filter: { equal: { field: NAME, value: \"my-app\" } }) { items { directDependencies { items { name } } } } }" | jq WARNING query command is experimental and may change in the future { "data": { - "packages": [ - { - "directDependencies": [ - { - "name": "util" + "packages": { + "items": [ + { + "directDependencies": { + "items": [ + { + "name": "util" + } + ] } - ] - } - ] + } + ] + } } } Get the indirect dependencies of `my-app` - $ ${TURBO} query "query { packages(filter: { equal: { field: NAME, value: \"my-app\" } }) { indirectDependencies { name } } }" | jq + $ ${TURBO} query "query { packages(filter: { equal: { field: NAME, value: \"my-app\" } }) { items { indirectDependencies { items { name } } } } }" | jq WARNING query command is experimental and may change in the future { "data": { - "packages": [ - { - "indirectDependencies": [ - { - "name": "//" + "packages": { + "items": [ + { + "indirectDependencies": { + "items": [ + { + "name": "//" + } + ] } - ] - } - ] + } + ] + } } } Get all dependencies of `my-app` - $ ${TURBO} query "query { packages(filter: { equal: { field: NAME, value: \"my-app\" } }) { allDependencies { name } } }" | jq + $ ${TURBO} query "query { packages(filter: { equal: { field: NAME, value: \"my-app\" } }) { items { allDependencies { items { name } } } } }" | jq WARNING query command is experimental and may change in the future { "data": { - "packages": [ - { - "allDependencies": [ - { - "name": "//" - }, - { - "name": "util" + "packages": { + "items": [ + { + "allDependencies": { + "items": [ + { + "name": "//" + }, + { + "name": "util" + } + ] } - ] - } - ] + } + ] + } } } Write query to file - $ echo 'query { packages { name } }' > query.gql + $ echo 'query { packages { items { name } } }' > query.gql Run the query $ ${TURBO} query query.gql | jq WARNING query command is experimental and may change in the future { "data": { - "packages": [ - { - "name": "//" - }, - { - "name": "another" - }, - { - "name": "my-app" - }, - { - "name": "util" - } - ] + "packages": { + "items": [ + { + "name": "//" + }, + { + "name": "another" + }, + { + "name": "my-app" + }, + { + "name": "util" + } + ] + } } } \ No newline at end of file