mirror of
https://github.com/graphql-hive/console
synced 2026-04-21 14:37:17 +00:00
Update dependency @theguild/prettier-config to v1 (#676)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Kamil Kisiela <kamil.kisiela@gmail.com>
This commit is contained in:
parent
85aa5ae783
commit
1afe0ec73a
420 changed files with 5562 additions and 2863 deletions
|
|
@ -1,8 +1,9 @@
|
|||
# Changesets
|
||||
|
||||
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
|
||||
with multi-package repos, or single-package repos to help you version and publish your code. You can
|
||||
find the full documentation for it [in our repository](https://github.com/changesets/changesets)
|
||||
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool
|
||||
that works with multi-package repos, or single-package repos to help you version and publish your
|
||||
code. You can find the full documentation for it
|
||||
[in our repository](https://github.com/changesets/changesets)
|
||||
|
||||
We have a quick list of common questions to get you started engaging with this project in
|
||||
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
|
||||
|
|
|
|||
|
|
@ -26,7 +26,10 @@ module.exports = {
|
|||
rules: {
|
||||
'no-process-env': 'error',
|
||||
'no-restricted-globals': ['error', 'stop'],
|
||||
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_', ignoreRestSiblings: true }],
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'error',
|
||||
{ argsIgnorePattern: '^_', ignoreRestSiblings: true },
|
||||
],
|
||||
'no-empty': ['error', { allowEmptyCatch: true }],
|
||||
|
||||
'import/no-absolute-path': 'error',
|
||||
|
|
|
|||
16
.github/workflows/cd.yaml
vendored
16
.github/workflows/cd.yaml
vendored
|
|
@ -45,7 +45,9 @@ jobs:
|
|||
run: pnpm build:libraries
|
||||
|
||||
- name: Schema Publish
|
||||
run: ./packages/libraries/cli/bin/dev schema:publish "packages/services/api/src/modules/*/module.graphql.ts" --force --github
|
||||
run:
|
||||
./packages/libraries/cli/bin/dev schema:publish
|
||||
"packages/services/api/src/modules/*/module.graphql.ts" --force --github
|
||||
|
||||
- name: Prepare NPM Credentials
|
||||
run: |
|
||||
|
|
@ -65,7 +67,9 @@ jobs:
|
|||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract published version
|
||||
if: steps.changesets.outputs.published && contains(steps.changesets.outputs.publishedPackages, '"@graphql-hive/cli"')
|
||||
if:
|
||||
steps.changesets.outputs.published && contains(steps.changesets.outputs.publishedPackages,
|
||||
'"@graphql-hive/cli"')
|
||||
id: cli
|
||||
run: |
|
||||
echo '${{steps.changesets.outputs.publishedPackages}}' > cli-ver.json
|
||||
|
|
@ -88,7 +92,9 @@ jobs:
|
|||
working-directory: packages/libraries/cli
|
||||
env:
|
||||
VERSION: ${{ steps.cli.outputs.version }}
|
||||
run: pnpm oclif promote --no-xz --sha ${GITHUB_SHA:0:7} --version $VERSION || pnpm oclif promote --no-xz --sha ${GITHUB_SHA:0:8} --version $VERSION
|
||||
run:
|
||||
pnpm oclif promote --no-xz --sha ${GITHUB_SHA:0:7} --version $VERSION || pnpm oclif
|
||||
promote --no-xz --sha ${GITHUB_SHA:0:8} --version $VERSION
|
||||
|
||||
publish_rust:
|
||||
name: Publish Rust
|
||||
|
|
@ -316,7 +322,9 @@ jobs:
|
|||
run: strip target/x86_64-unknown-linux-gnu/release/router
|
||||
|
||||
- name: Compress
|
||||
run: ./target/x86_64-unknown-linux-gnu/release/compress ./target/x86_64-unknown-linux-gnu/release/router ./router.tar.gz
|
||||
run:
|
||||
./target/x86_64-unknown-linux-gnu/release/compress
|
||||
./target/x86_64-unknown-linux-gnu/release/router ./router.tar.gz
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
|
|
|
|||
14
.github/workflows/ci.yaml
vendored
14
.github/workflows/ci.yaml
vendored
|
|
@ -17,10 +17,7 @@ jobs:
|
|||
POSTGRES_USER: postgres
|
||||
POSTGRES_DB: registry
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
--health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
|
||||
|
||||
outputs:
|
||||
hive_token_present: ${{ steps.secrets_present.outputs.hive_token }}
|
||||
|
|
@ -128,7 +125,10 @@ jobs:
|
|||
id: pr-label-check
|
||||
|
||||
- name: Schema Check
|
||||
run: ./packages/libraries/cli/bin/dev schema:check "packages/services/api/src/modules/*/module.graphql.ts" ${{ steps.pr-label-check.outputs.SAFE_FLAG }} --github
|
||||
run:
|
||||
./packages/libraries/cli/bin/dev schema:check
|
||||
"packages/services/api/src/modules/*/module.graphql.ts" ${{
|
||||
steps.pr-label-check.outputs.SAFE_FLAG }} --github
|
||||
|
||||
test:
|
||||
name: Tests
|
||||
|
|
@ -283,7 +283,9 @@ jobs:
|
|||
run: strip target/x86_64-unknown-linux-gnu/release/router
|
||||
|
||||
- name: Compress
|
||||
run: ./target/x86_64-unknown-linux-gnu/release/compress ./target/x86_64-unknown-linux-gnu/release/router ./router.tar.gz
|
||||
run:
|
||||
./target/x86_64-unknown-linux-gnu/release/compress
|
||||
./target/x86_64-unknown-linux-gnu/release/router ./router.tar.gz
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
|
|
|
|||
|
|
@ -2,30 +2,43 @@
|
|||
|
||||
## 1. Purpose
|
||||
|
||||
A primary goal of GraphQL Hive is to be inclusive to the largest number of contributors, with the most varied and diverse backgrounds possible. As such, we are committed to providing a friendly, safe and welcoming environment for all, regardless of gender, sexual orientation, ability, ethnicity, socioeconomic status, and religion (or lack thereof).
|
||||
A primary goal of GraphQL Hive is to be inclusive to the largest number of contributors, with the
|
||||
most varied and diverse backgrounds possible. As such, we are committed to providing a friendly,
|
||||
safe and welcoming environment for all, regardless of gender, sexual orientation, ability,
|
||||
ethnicity, socioeconomic status, and religion (or lack thereof).
|
||||
|
||||
This code of conduct outlines our expectations for all those who participate in our community, as well as the consequences for unacceptable behavior.
|
||||
This code of conduct outlines our expectations for all those who participate in our community, as
|
||||
well as the consequences for unacceptable behavior.
|
||||
|
||||
We invite all those who participate in GraphQL Hive to help us create safe and positive experiences for everyone.
|
||||
We invite all those who participate in GraphQL Hive to help us create safe and positive experiences
|
||||
for everyone.
|
||||
|
||||
## 2. Open [Source/Culture/Tech] Citizenship
|
||||
|
||||
A supplemental goal of this Code of Conduct is to increase open [source/culture/tech] citizenship by encouraging participants to recognize and strengthen the relationships between our actions and their effects on our community.
|
||||
A supplemental goal of this Code of Conduct is to increase open [source/culture/tech] citizenship by
|
||||
encouraging participants to recognize and strengthen the relationships between our actions and their
|
||||
effects on our community.
|
||||
|
||||
Communities mirror the societies in which they exist and positive action is essential to counteract the many forms of inequality and abuses of power that exist in society.
|
||||
Communities mirror the societies in which they exist and positive action is essential to counteract
|
||||
the many forms of inequality and abuses of power that exist in society.
|
||||
|
||||
If you see someone who is making an extra effort to ensure our community is welcoming, friendly, and encourages all participants to contribute to the fullest extent, we want to know.
|
||||
If you see someone who is making an extra effort to ensure our community is welcoming, friendly, and
|
||||
encourages all participants to contribute to the fullest extent, we want to know.
|
||||
|
||||
## 3. Expected Behavior
|
||||
|
||||
The following behaviors are expected and requested of all community members:
|
||||
|
||||
- Participate in an authentic and active way. In doing so, you contribute to the health and longevity of this community.
|
||||
- Participate in an authentic and active way. In doing so, you contribute to the health and
|
||||
longevity of this community.
|
||||
- Exercise consideration and respect in your speech and actions.
|
||||
- Attempt collaboration before conflict.
|
||||
- Refrain from demeaning, discriminatory, or harassing behavior and speech.
|
||||
- Be mindful of your surroundings and of your fellow participants. Alert community leaders if you notice a dangerous situation, someone in distress, or violations of this Code of Conduct, even if they seem inconsequential.
|
||||
- Remember that community event venues may be shared with members of the public; please be respectful to all patrons of these locations.
|
||||
- Be mindful of your surroundings and of your fellow participants. Alert community leaders if you
|
||||
notice a dangerous situation, someone in distress, or violations of this Code of Conduct, even if
|
||||
they seem inconsequential.
|
||||
- Remember that community event venues may be shared with members of the public; please be
|
||||
respectful to all patrons of these locations.
|
||||
|
||||
## 4. Unacceptable Behavior
|
||||
|
||||
|
|
@ -35,41 +48,62 @@ The following behaviors are considered harassment and are unacceptable within ou
|
|||
- Sexist, racist, homophobic, transphobic, ableist or otherwise discriminatory jokes and language.
|
||||
- Posting or displaying sexually explicit or violent material.
|
||||
- Posting or threatening to post other people's personally identifying information ("doxing").
|
||||
- Personal insults, particularly those related to gender, sexual orientation, race, religion, or disability.
|
||||
- Personal insults, particularly those related to gender, sexual orientation, race, religion, or
|
||||
disability.
|
||||
- Inappropriate photography or recording.
|
||||
- Inappropriate physical contact. You should have someone's consent before touching them.
|
||||
- Unwelcome sexual attention. This includes, sexualized comments or jokes; inappropriate touching, groping, and unwelcomed sexual advances.
|
||||
- Unwelcome sexual attention. This includes, sexualized comments or jokes; inappropriate touching,
|
||||
groping, and unwelcomed sexual advances.
|
||||
- Deliberate intimidation, stalking or following (online or in person).
|
||||
- Advocating for, or encouraging, any of the above behavior.
|
||||
- Sustained disruption of community events, including talks and presentations.
|
||||
|
||||
## 5. Weapons Policy
|
||||
|
||||
No weapons will be allowed at GraphQL Hive events, community spaces, or in other spaces covered by the scope of this Code of Conduct. Weapons include but are not limited to guns, explosives (including fireworks), and large knives such as those used for hunting or display, as well as any other item used for the purpose of causing injury or harm to others. Anyone seen in possession of one of these items will be asked to leave immediately, and will only be allowed to return without the weapon. Community members are further expected to comply with all state and local laws on this matter.
|
||||
No weapons will be allowed at GraphQL Hive events, community spaces, or in other spaces covered by
|
||||
the scope of this Code of Conduct. Weapons include but are not limited to guns, explosives
|
||||
(including fireworks), and large knives such as those used for hunting or display, as well as any
|
||||
other item used for the purpose of causing injury or harm to others. Anyone seen in possession of
|
||||
one of these items will be asked to leave immediately, and will only be allowed to return without
|
||||
the weapon. Community members are further expected to comply with all state and local laws on this
|
||||
matter.
|
||||
|
||||
## 6. Consequences of Unacceptable Behavior
|
||||
|
||||
Unacceptable behavior from any community member, including sponsors and those with decision-making authority, will not be tolerated.
|
||||
Unacceptable behavior from any community member, including sponsors and those with decision-making
|
||||
authority, will not be tolerated.
|
||||
|
||||
Anyone asked to stop unacceptable behavior is expected to comply immediately.
|
||||
|
||||
If a community member engages in unacceptable behavior, the community organizers may take any action they deem appropriate, up to and including a temporary ban or permanent expulsion from the community without warning (and without refund in the case of a paid event).
|
||||
If a community member engages in unacceptable behavior, the community organizers may take any action
|
||||
they deem appropriate, up to and including a temporary ban or permanent expulsion from the community
|
||||
without warning (and without refund in the case of a paid event).
|
||||
|
||||
## 7. Reporting Guidelines
|
||||
|
||||
If you are subject to or witness unacceptable behavior, or have any other concerns, please notify a community organizer as soon as possible. contact@the-guild.dev.
|
||||
If you are subject to or witness unacceptable behavior, or have any other concerns, please notify a
|
||||
community organizer as soon as possible. contact@the-guild.dev.
|
||||
|
||||
Additionally, community organizers are available to help community members engage with local law enforcement or to otherwise help those experiencing unacceptable behavior feel safe. In the context of in-person events, organizers will also provide escorts as desired by the person experiencing distress.
|
||||
Additionally, community organizers are available to help community members engage with local law
|
||||
enforcement or to otherwise help those experiencing unacceptable behavior feel safe. In the context
|
||||
of in-person events, organizers will also provide escorts as desired by the person experiencing
|
||||
distress.
|
||||
|
||||
## 8. Addressing Grievances
|
||||
|
||||
If you feel you have been falsely or unfairly accused of violating this Code of Conduct, you should notify with a concise description of your grievance. Your grievance will be handled in accordance with our existing governing policies. [Policy](https://graphql-hive.com/privacy-policy.pdf)
|
||||
If you feel you have been falsely or unfairly accused of violating this Code of Conduct, you should
|
||||
notify with a concise description of your grievance. Your grievance will be handled in accordance
|
||||
with our existing governing policies. [Policy](https://graphql-hive.com/privacy-policy.pdf)
|
||||
|
||||
## 9. Scope
|
||||
|
||||
We expect all community participants (contributors, paid or otherwise; sponsors; and other guests) to abide by this Code of Conduct in all community venues--online and in-person--as well as in all one-on-one communications pertaining to community business.
|
||||
We expect all community participants (contributors, paid or otherwise; sponsors; and other guests)
|
||||
to abide by this Code of Conduct in all community venues--online and in-person--as well as in all
|
||||
one-on-one communications pertaining to community business.
|
||||
|
||||
This code of conduct and its related procedures also applies to unacceptable behavior occurring outside the scope of community activities when such behavior has the potential to adversely affect the safety and well-being of community members.
|
||||
This code of conduct and its related procedures also applies to unacceptable behavior occurring
|
||||
outside the scope of community activities when such behavior has the potential to adversely affect
|
||||
the safety and well-being of community members.
|
||||
|
||||
## 10. Contact info
|
||||
|
||||
|
|
@ -77,9 +111,13 @@ contact@the-guild.dev
|
|||
|
||||
## 11. License and attribution
|
||||
|
||||
The Citizen Code of Conduct is distributed by [Stumptown Syndicate](http://stumptownsyndicate.org) under a [Creative Commons Attribution-ShareAlike license](http://creativecommons.org/licenses/by-sa/3.0/).
|
||||
The Citizen Code of Conduct is distributed by [Stumptown Syndicate](http://stumptownsyndicate.org)
|
||||
under a
|
||||
[Creative Commons Attribution-ShareAlike license](http://creativecommons.org/licenses/by-sa/3.0/).
|
||||
|
||||
Portions of text derived from the [Django Code of Conduct](https://www.djangoproject.com/conduct/) and the [Geek Feminism Anti-Harassment Policy](http://geekfeminism.wikia.com/wiki/Conference_anti-harassment/Policy).
|
||||
Portions of text derived from the [Django Code of Conduct](https://www.djangoproject.com/conduct/)
|
||||
and the
|
||||
[Geek Feminism Anti-Harassment Policy](http://geekfeminism.wikia.com/wiki/Conference_anti-harassment/Policy).
|
||||
|
||||
_Revision 2.3. Posted 6 March 2017._
|
||||
|
||||
|
|
@ -87,4 +125,5 @@ _Revision 2.2. Posted 4 February 2016._
|
|||
|
||||
_Revision 2.1. Posted 23 June 2014._
|
||||
|
||||
_Revision 2.0, adopted by the [Stumptown Syndicate](http://stumptownsyndicate.org) board on 10 January 2013. Posted 17 March 2013._
|
||||
_Revision 2.0, adopted by the [Stumptown Syndicate](http://stumptownsyndicate.org) board on 10
|
||||
January 2013. Posted 17 March 2013._
|
||||
|
|
|
|||
33
README.md
33
README.md
|
|
@ -1,8 +1,10 @@
|
|||
# GraphQL Hive
|
||||
|
||||
GraphQL Hive provides all the tools the get visibility of your GraphQL architecture at all stages, from standalone APIs to composed schemas (Federation, Stitching).
|
||||
GraphQL Hive provides all the tools the get visibility of your GraphQL architecture at all stages,
|
||||
from standalone APIs to composed schemas (Federation, Stitching).
|
||||
|
||||
- Visit [graphql-hive.com](https://graphql-hive.com) ([status page](https://status.graphql-hive.com/))
|
||||
- Visit [graphql-hive.com](https://graphql-hive.com)
|
||||
([status page](https://status.graphql-hive.com/))
|
||||
- [Read the announcement blog post](https://www.the-guild.dev/blog/announcing-graphql-hive-public)
|
||||
- [Read the docs](https://docs.graphql-hive.com)
|
||||
|
||||
|
|
@ -10,10 +12,12 @@ GraphQL Hive provides all the tools the get visibility of your GraphQL architect
|
|||
|
||||
GraphQL Hive has been built with 3 main objectives in mind:
|
||||
|
||||
- **Help GraphQL developers to get to know their GraphQL APIs** a little more with our Schema Registry, Performance Monitoring, Alerts, and Integrations.
|
||||
- **Help GraphQL developers to get to know their GraphQL APIs** a little more with our Schema
|
||||
Registry, Performance Monitoring, Alerts, and Integrations.
|
||||
- **Support all kinds of GraphQL APIs**, from Federation, and Stitching, to standalone APIs.
|
||||
- **Open Source at the heart**: 100% open-source and build in public with the community.
|
||||
- **A plug and play SaaS solution**: to give access to Hive to most people with a generous free "Hobby plan"
|
||||
- **A plug and play SaaS solution**: to give access to Hive to most people with a generous free
|
||||
"Hobby plan"
|
||||
|
||||
## Features Overview
|
||||
|
||||
|
|
@ -21,18 +25,22 @@ GraphQL Hive has been built with 3 main objectives in mind:
|
|||
|
||||
GraphQL Hive offers 3 useful features to manage your GraphQL API:
|
||||
|
||||
- **Prevent breaking changes** - GraphQL Hive will run a set of checks and notify your team via Slack, GitHub, or within the application.
|
||||
- **Prevent breaking changes** - GraphQL Hive will run a set of checks and notify your team via
|
||||
Slack, GitHub, or within the application.
|
||||
- **Data-driven** definition of a “breaking change” based on Operations Monitoring.
|
||||
- **History of changes** - an access to the full history of changes, even on a complex composed schema (Federation, Stitching).
|
||||
- **History of changes** - an access to the full history of changes, even on a complex composed
|
||||
schema (Federation, Stitching).
|
||||
- **High-availability and multi-zone CDN** service based on Cloudflare to access Schema Registry
|
||||
|
||||
### Monitoring
|
||||
|
||||
Once a Schema is deployed, **it is important to be aware of how it is used and what is the experience of its final users**.
|
||||
Once a Schema is deployed, **it is important to be aware of how it is used and what is the
|
||||
experience of its final users**.
|
||||
|
||||
## Self-hosted
|
||||
|
||||
GraphQL Hive is completely open-source under the MIT license, meaning that you are free to host on your own infrastructure.
|
||||
GraphQL Hive is completely open-source under the MIT license, meaning that you are free to host on
|
||||
your own infrastructure.
|
||||
|
||||
GraphQL Hive helps you get a global overview of the usage of your GraphQL API with:
|
||||
|
||||
|
|
@ -43,13 +51,16 @@ GraphQL Hive helps you get a global overview of the usage of your GraphQL API wi
|
|||
|
||||
### Integrations
|
||||
|
||||
GraphQL Hive is well integrated with **Slack** and most **CI/CD** systems to get you up and running as smoothly as possible!
|
||||
GraphQL Hive is well integrated with **Slack** and most **CI/CD** systems to get you up and running
|
||||
as smoothly as possible!
|
||||
|
||||
GraphQL Hive can notify your team when schema changes occur, either via Slack or a custom webhook.
|
||||
|
||||
Also, the Hive CLI allows integration of the schema checks mechanism to all CI/CD systems (GitHub, BitBucket, Azure, and others). The same applies for schema publishing and operations checks.
|
||||
Also, the Hive CLI allows integration of the schema checks mechanism to all CI/CD systems (GitHub,
|
||||
BitBucket, Azure, and others). The same applies for schema publishing and operations checks.
|
||||
|
||||
If you are using GitHub, you can directly benefit from the **GraphQL Hive app that will automatically add status checks to your PRs**!
|
||||
If you are using GitHub, you can directly benefit from the **GraphQL Hive app that will
|
||||
automatically add status checks to your PRs**!
|
||||
|
||||
### Join us in building the future of GraphQL Hive
|
||||
|
||||
|
|
|
|||
30
codegen.yml
30
codegen.yml
|
|
@ -21,9 +21,12 @@ generates:
|
|||
DateTime: string
|
||||
SafeInt: number
|
||||
mappers:
|
||||
SchemaChangeConnection: ../shared/mappers#SchemaChangeConnection as SchemaChangeConnectionMapper
|
||||
SchemaErrorConnection: ../shared/mappers#SchemaErrorConnection as SchemaErrorConnectionMapper
|
||||
OrganizationConnection: ../shared/mappers#OrganizationConnection as OrganizationConnectionMapper
|
||||
SchemaChangeConnection:
|
||||
../shared/mappers#SchemaChangeConnection as SchemaChangeConnectionMapper
|
||||
SchemaErrorConnection:
|
||||
../shared/mappers#SchemaErrorConnection as SchemaErrorConnectionMapper
|
||||
OrganizationConnection:
|
||||
../shared/mappers#OrganizationConnection as OrganizationConnectionMapper
|
||||
UserConnection: ../shared/mappers#UserConnection as UserConnectionMapper
|
||||
ActivityConnection: ../shared/mappers#ActivityConnection as ActivityConnectionMapper
|
||||
MemberConnection: ../shared/mappers#MemberConnection as MemberConnectionMapper
|
||||
|
|
@ -31,16 +34,20 @@ generates:
|
|||
TargetConnection: ../shared/mappers#TargetConnection as TargetConnectionMapper
|
||||
SchemaConnection: ../shared/mappers#SchemaConnection as SchemaConnectionMapper
|
||||
TokenConnection: ../shared/mappers#TokenConnection as TokenConnectionMapper
|
||||
OperationStatsConnection: ../shared/mappers#OperationStatsConnection as OperationStatsConnectionMapper
|
||||
ClientStatsConnection: ../shared/mappers#ClientStatsConnection as ClientStatsConnectionMapper
|
||||
OperationStatsConnection:
|
||||
../shared/mappers#OperationStatsConnection as OperationStatsConnectionMapper
|
||||
ClientStatsConnection:
|
||||
../shared/mappers#ClientStatsConnection as ClientStatsConnectionMapper
|
||||
OperationsStats: ../shared/mappers#OperationsStats as OperationsStatsMapper
|
||||
DurationStats: ../shared/mappers#DurationStats as DurationStatsMapper
|
||||
SchemaComparePayload: ../shared/mappers#SchemaComparePayload as SchemaComparePayloadMapper
|
||||
SchemaCompareResult: ../shared/mappers#SchemaCompareResult as SchemaCompareResultMapper
|
||||
SchemaVersionConnection: ../shared/mappers#SchemaVersionConnection as SchemaVersionConnectionMapper
|
||||
SchemaVersionConnection:
|
||||
../shared/mappers#SchemaVersionConnection as SchemaVersionConnectionMapper
|
||||
SchemaVersion: ../shared/mappers#SchemaVersion as SchemaVersionMapper
|
||||
Schema: ../shared/mappers#Schema as SchemaMapper
|
||||
PersistedOperationConnection: ../shared/mappers#PersistedOperationConnection as PersistedOperationMapper
|
||||
PersistedOperationConnection:
|
||||
../shared/mappers#PersistedOperationConnection as PersistedOperationMapper
|
||||
Organization: ../shared/entities#Organization as OrganizationMapper
|
||||
Project: ../shared/entities#Project as ProjectMapper
|
||||
Target: ../shared/entities#Target as TargetMapper
|
||||
|
|
@ -55,13 +62,15 @@ generates:
|
|||
AdminQuery: '{}'
|
||||
AdminStats: '{ daysLimit?: number | null }'
|
||||
AdminGeneralStats: '{ daysLimit?: number | null }'
|
||||
AdminOrganizationStats: ../shared/entities#AdminOrganizationStats as AdminOrganizationStatsMapper
|
||||
AdminOrganizationStats:
|
||||
../shared/entities#AdminOrganizationStats as AdminOrganizationStatsMapper
|
||||
UsageEstimation: '../shared/mappers#TargetsEstimationFilter'
|
||||
UsageEstimationScope: '../shared/mappers#TargetsEstimationDateFilter'
|
||||
BillingPaymentMethod: 'StripeTypes.PaymentMethod.Card'
|
||||
BillingDetails: 'StripeTypes.PaymentMethod.BillingDetails'
|
||||
BillingInvoice: 'StripeTypes.Invoice'
|
||||
OrganizationGetStarted: ../shared/entities#OrganizationGetStarted as OrganizationGetStartedMapper
|
||||
OrganizationGetStarted:
|
||||
../shared/entities#OrganizationGetStarted as OrganizationGetStartedMapper
|
||||
SchemaExplorer: ../shared/mappers#SchemaExplorerMapper
|
||||
GraphQLObjectType: ../shared/mappers#GraphQLObjectTypeMapper
|
||||
GraphQLInterfaceType: ../shared/mappers#GraphQLInterfaceTypeMapper
|
||||
|
|
@ -74,7 +83,8 @@ generates:
|
|||
GraphQLField: ../shared/mappers#GraphQLFieldMapper
|
||||
GraphQLInputField: ../shared/mappers#GraphQLInputFieldMapper
|
||||
GraphQLArgument: ../shared/mappers#GraphQLArgumentMapper
|
||||
OrganizationInvitation: ../shared/entities#OrganizationInvitation as OrganizationInvitationMapper
|
||||
OrganizationInvitation:
|
||||
../shared/entities#OrganizationInvitation as OrganizationInvitationMapper
|
||||
OIDCIntegration: '../shared/entities#OIDCIntegration as OIDCIntegrationMapper'
|
||||
User: '../shared/entities#User as UserMapper'
|
||||
plugins:
|
||||
|
|
|
|||
|
|
@ -182,8 +182,14 @@ const schemaApi = deploySchema({
|
|||
broker: cfBroker,
|
||||
});
|
||||
|
||||
const supertokensApiKey = new random.RandomPassword('supertokens-api-key', { length: 31, special: false });
|
||||
const auth0LegacyMigrationKey = new random.RandomPassword('auth0-legacy-migration-key', { length: 69, special: false });
|
||||
const supertokensApiKey = new random.RandomPassword('supertokens-api-key', {
|
||||
length: 31,
|
||||
special: false,
|
||||
});
|
||||
const auth0LegacyMigrationKey = new random.RandomPassword('auth0-legacy-migration-key', {
|
||||
length: 69,
|
||||
special: false,
|
||||
});
|
||||
|
||||
const oauthConfig = new pulumi.Config('oauth');
|
||||
|
||||
|
|
|
|||
|
|
@ -4,10 +4,6 @@
|
|||
"scripts": {
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mime-types": "2.1.1",
|
||||
"@types/node": "18.11.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@manypkg/get-packages": "1.1.3",
|
||||
"@pulumi/azure": "5.23.0",
|
||||
|
|
@ -17,5 +13,9 @@
|
|||
"@pulumi/pulumi": "3.44.1",
|
||||
"@pulumi/random": "4.8.2",
|
||||
"pg-connection-string": "2.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mime-types": "2.1.1",
|
||||
"@types/node": "18.11.5"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -206,6 +206,6 @@ export function deployApp({
|
|||
],
|
||||
port: 3000,
|
||||
},
|
||||
[graphql.service, graphql.deployment, dbMigrations]
|
||||
[graphql.service, graphql.deployment, dbMigrations],
|
||||
).deploy();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,9 @@ export function deployStripeBilling({
|
|||
dbMigrations: DbMigrations;
|
||||
}) {
|
||||
const rawConnectionString = apiConfig.requireSecret('postgresConnectionString');
|
||||
const connectionString = rawConnectionString.apply(rawConnectionString => parse(rawConnectionString));
|
||||
const connectionString = rawConnectionString.apply(rawConnectionString =>
|
||||
parse(rawConnectionString),
|
||||
);
|
||||
|
||||
return new RemoteArtifactAsServiceDeployment(
|
||||
'stripe-billing',
|
||||
|
|
@ -56,6 +58,6 @@ export function deployStripeBilling({
|
|||
packageInfo: packageHelper.npmPack('@hive/stripe-billing'),
|
||||
port: 4000,
|
||||
},
|
||||
[dbMigrations, usageEstimator.service, usageEstimator.deployment]
|
||||
[dbMigrations, usageEstimator.service, usageEstimator.deployment],
|
||||
).deploy();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,11 +7,16 @@ function toExpressionList(items: string[]): string {
|
|||
return items.map(v => `"${v}"`).join(' ');
|
||||
}
|
||||
|
||||
export function deployCloudFlareSecurityTransform(options: { envName: string; ignoredPaths: string[] }) {
|
||||
export function deployCloudFlareSecurityTransform(options: {
|
||||
envName: string;
|
||||
ignoredPaths: string[];
|
||||
}) {
|
||||
// We deploy it only once, because CloudFlare is not super friendly for multiple deployments of "http_response_headers_transform" rules
|
||||
// The single rule, deployed to prod, covers all other envs, and infers the hostname dynamically.
|
||||
if (options.envName !== 'prod') {
|
||||
console.warn(`Skipped deploy security headers (see "cloudflare-security.ts") for env ${options.envName}`);
|
||||
console.warn(
|
||||
`Skipped deploy security headers (see "cloudflare-security.ts") for env ${options.envName}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,9 @@ export function deployDbMigrations({
|
|||
kafka: Kafka;
|
||||
}) {
|
||||
const rawConnectionString = apiConfig.requireSecret('postgresConnectionString');
|
||||
const connectionString = rawConnectionString.apply(rawConnectionString => parse(rawConnectionString));
|
||||
const connectionString = rawConnectionString.apply(rawConnectionString =>
|
||||
parse(rawConnectionString),
|
||||
);
|
||||
|
||||
const { job } = new RemoteArtifactAsServiceDeployment(
|
||||
'db-migrations',
|
||||
|
|
@ -50,7 +52,7 @@ export function deployDbMigrations({
|
|||
packageInfo: packageHelper.npmPack('@hive/storage'),
|
||||
},
|
||||
[clickhouse.deployment, clickhouse.service],
|
||||
clickhouse.service
|
||||
clickhouse.service,
|
||||
).deployAsJob();
|
||||
|
||||
return job;
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ export function deployEmails({
|
|||
packageInfo: packageHelper.npmPack('@hive/emails'),
|
||||
replicas: 1,
|
||||
},
|
||||
[redis.deployment, redis.service]
|
||||
[redis.deployment, redis.service],
|
||||
).deploy();
|
||||
|
||||
return { deployment, service, localEndpoint: serviceLocalEndpoint(service) };
|
||||
|
|
|
|||
|
|
@ -72,7 +72,9 @@ export function deployGraphQL({
|
|||
};
|
||||
}) {
|
||||
const rawConnectionString = apiConfig.requireSecret('postgresConnectionString');
|
||||
const connectionString = rawConnectionString.apply(rawConnectionString => parse(rawConnectionString));
|
||||
const connectionString = rawConnectionString.apply(rawConnectionString =>
|
||||
parse(rawConnectionString),
|
||||
);
|
||||
|
||||
return new RemoteArtifactAsServiceDeployment(
|
||||
'graphql-api',
|
||||
|
|
@ -148,6 +150,6 @@ export function deployGraphQL({
|
|||
clickhouse.service,
|
||||
rateLimit.deployment,
|
||||
rateLimit.service,
|
||||
]
|
||||
],
|
||||
).deploy();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ export function deployCloudflarePolice({ envName, rootDns }: { envName: string;
|
|||
cfCustomConfig.require('zoneId'),
|
||||
cloudflareProviderConfig.require('accountId'),
|
||||
cfCustomConfig.requireSecret('policeApiToken'),
|
||||
rootDns
|
||||
rootDns,
|
||||
);
|
||||
|
||||
return police.deploy();
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ export function deployProxy({
|
|||
path: '/',
|
||||
service: docs.service,
|
||||
},
|
||||
]
|
||||
],
|
||||
)
|
||||
.registerService({ record: appHostname }, [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -32,7 +32,9 @@ export function deployRateLimit({
|
|||
emails: Emails;
|
||||
}) {
|
||||
const rawConnectionString = apiConfig.requireSecret('postgresConnectionString');
|
||||
const connectionString = rawConnectionString.apply(rawConnectionString => parse(rawConnectionString));
|
||||
const connectionString = rawConnectionString.apply(rawConnectionString =>
|
||||
parse(rawConnectionString),
|
||||
);
|
||||
|
||||
return new RemoteArtifactAsServiceDeployment(
|
||||
'rate-limiter',
|
||||
|
|
@ -60,6 +62,6 @@ export function deployRateLimit({
|
|||
packageInfo: packageHelper.npmPack('@hive/rate-limit'),
|
||||
port: 4000,
|
||||
},
|
||||
[dbMigrations, usageEstimator.service, usageEstimator.deployment]
|
||||
[dbMigrations, usageEstimator.service, usageEstimator.deployment],
|
||||
).deploy();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,6 +48,6 @@ export function deploySchema({
|
|||
replicas: 2,
|
||||
pdb: true,
|
||||
},
|
||||
[redis.deployment, redis.service]
|
||||
[redis.deployment, redis.service],
|
||||
).deploy();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,9 @@ export function deployTokens({
|
|||
heartbeat?: string;
|
||||
}) {
|
||||
const rawConnectionString = apiConfig.requireSecret('postgresConnectionString');
|
||||
const connectionString = rawConnectionString.apply(rawConnectionString => parse(rawConnectionString));
|
||||
const connectionString = rawConnectionString.apply(rawConnectionString =>
|
||||
parse(rawConnectionString),
|
||||
);
|
||||
|
||||
return new RemoteArtifactAsServiceDeployment(
|
||||
'tokens-service',
|
||||
|
|
@ -50,6 +52,6 @@ export function deployTokens({
|
|||
HEARTBEAT_ENDPOINT: heartbeat ?? '',
|
||||
},
|
||||
},
|
||||
[dbMigrations]
|
||||
[dbMigrations],
|
||||
).deploy();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,6 +46,6 @@ export function deployUsageEstimation({
|
|||
packageInfo: packageHelper.npmPack('@hive/usage-estimator'),
|
||||
port: 4000,
|
||||
},
|
||||
[dbMigrations]
|
||||
[dbMigrations],
|
||||
).deploy();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,6 +87,6 @@ export function deployUsageIngestor({
|
|||
maxReplicas: maxReplicas,
|
||||
},
|
||||
},
|
||||
[clickhouse.deployment, clickhouse.service, dbMigrations]
|
||||
[clickhouse.deployment, clickhouse.service, dbMigrations],
|
||||
).deploy();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,6 +73,6 @@ export function deployUsage({
|
|||
maxReplicas: maxReplicas,
|
||||
},
|
||||
},
|
||||
[dbMigrations, tokens.deployment, tokens.service, rateLimit.deployment, rateLimit.service]
|
||||
[dbMigrations, tokens.deployment, tokens.service, rateLimit.deployment, rateLimit.service],
|
||||
).deploy();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,6 +50,6 @@ export function deployWebhooks({
|
|||
packageInfo: packageHelper.npmPack('@hive/webhooks'),
|
||||
replicas: 1,
|
||||
},
|
||||
[redis.deployment, redis.service]
|
||||
[redis.deployment, redis.service],
|
||||
).deploy();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ export class BotKube {
|
|||
},
|
||||
{
|
||||
dependsOn: [ns],
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ export class CertManager {
|
|||
},
|
||||
{
|
||||
dependsOn: [certManager],
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ export class Clickhouse {
|
|||
protected options: {
|
||||
env?: kx.types.Container['env'];
|
||||
sentryDsn: string;
|
||||
}
|
||||
},
|
||||
) {}
|
||||
|
||||
deploy() {
|
||||
|
|
@ -79,7 +79,7 @@ export class Clickhouse {
|
|||
},
|
||||
{
|
||||
annotations: metadata.annotations,
|
||||
}
|
||||
},
|
||||
),
|
||||
});
|
||||
const service = deployment.createService({});
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export class CloudflareCDN {
|
|||
authPrivateKey: pulumi.Output<string>;
|
||||
sentryDsn: string;
|
||||
release: string;
|
||||
}
|
||||
},
|
||||
) {}
|
||||
|
||||
deploy() {
|
||||
|
|
@ -21,7 +21,10 @@ export class CloudflareCDN {
|
|||
});
|
||||
|
||||
const script = new cf.WorkerScript('hive-ha-worker', {
|
||||
content: readFileSync(resolve(__dirname, '../../packages/services/cdn-worker/dist/worker.js'), 'utf-8'),
|
||||
content: readFileSync(
|
||||
resolve(__dirname, '../../packages/services/cdn-worker/dist/worker.js'),
|
||||
'utf-8',
|
||||
),
|
||||
name: `hive-storage-cdn-${this.config.envName}`,
|
||||
kvNamespaceBindings: [
|
||||
{
|
||||
|
|
@ -78,12 +81,15 @@ export class CloudflareBroker {
|
|||
secretSignature: pulumi.Output<string>;
|
||||
sentryDsn: string;
|
||||
release: string;
|
||||
}
|
||||
},
|
||||
) {}
|
||||
|
||||
deploy() {
|
||||
const script = new cf.WorkerScript('hive-broker-worker', {
|
||||
content: readFileSync(resolve(__dirname, '../../packages/services/broker-worker/dist/worker.js'), 'utf-8'),
|
||||
content: readFileSync(
|
||||
resolve(__dirname, '../../packages/services/broker-worker/dist/worker.js'),
|
||||
'utf-8',
|
||||
),
|
||||
name: `hive-broker-${this.config.envName}`,
|
||||
secretTextBindings: [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -5,7 +5,9 @@ export function isProduction(deploymentEnv: DeploymentEnvironment | string): boo
|
|||
}
|
||||
|
||||
export function isStaging(deploymentEnv: DeploymentEnvironment | string): boolean {
|
||||
return isDeploymentEnvironment(deploymentEnv) ? deploymentEnv.ENVIRONMENT === 'staging' : deploymentEnv === 'staging';
|
||||
return isDeploymentEnvironment(deploymentEnv)
|
||||
? deploymentEnv.ENVIRONMENT === 'staging'
|
||||
: deploymentEnv === 'staging';
|
||||
}
|
||||
|
||||
export function isDeploymentEnvironment(value: any): value is DeploymentEnvironment {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@ export function serviceLocalEndpoint(service: k8s.types.input.core.v1.Service) {
|
|||
const defaultPort = (spec?.ports || [])[0];
|
||||
const portText = defaultPort ? `:${defaultPort.port}` : '';
|
||||
|
||||
return `http://${metadata?.name}.${metadata?.namespace || 'default'}.svc.cluster.local${portText}`;
|
||||
return `http://${metadata?.name}.${
|
||||
metadata?.namespace || 'default'
|
||||
}.svc.cluster.local${portText}`;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -210,7 +210,10 @@ export class Observability {
|
|||
regex: '(.+)',
|
||||
},
|
||||
{
|
||||
source_labels: ['__address__', '__meta_kubernetes_pod_annotation_prometheus_io_port'],
|
||||
source_labels: [
|
||||
'__address__',
|
||||
'__meta_kubernetes_pod_annotation_prometheus_io_port',
|
||||
],
|
||||
action: 'replace',
|
||||
regex: '([^:]+)(?::d+)?;(d+)',
|
||||
replacement: '$1:$2',
|
||||
|
|
@ -358,7 +361,7 @@ export class Observability {
|
|||
},
|
||||
{
|
||||
dependsOn: [ns],
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ export function normalizeEnv(env: kx.types.Container['env']): any[] {
|
|||
export class PodBuilder extends kx.PodBuilder {
|
||||
public asExtendedDeploymentSpec(
|
||||
args?: kx.types.PodBuilderDeploymentSpec,
|
||||
metadata?: k8s.types.input.meta.v1.ObjectMeta
|
||||
metadata?: k8s.types.input.meta.v1.ObjectMeta,
|
||||
): pulumi.Output<k8s.types.input.apps.v1.DeploymentSpec> {
|
||||
const podName = this.podSpec.containers.apply((containers: any) => {
|
||||
return pulumi.output(containers[0].name);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ export class HivePolice {
|
|||
private zoneId: string,
|
||||
private accountId: string,
|
||||
private cfToken: pulumi.Output<string>,
|
||||
private rootDns: string
|
||||
private rootDns: string,
|
||||
) {}
|
||||
|
||||
deploy() {
|
||||
|
|
@ -18,7 +18,10 @@ export class HivePolice {
|
|||
});
|
||||
|
||||
const script = new cf.WorkerScript('hive-police-worker', {
|
||||
content: readFileSync(resolve(__dirname, '../../packages/services/police-worker/dist/worker.js'), 'utf-8'),
|
||||
content: readFileSync(
|
||||
resolve(__dirname, '../../packages/services/police-worker/dist/worker.js'),
|
||||
'utf-8',
|
||||
),
|
||||
name: `hive-police-${this.envName}`,
|
||||
kvNamespaceBindings: [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ export class Redis {
|
|||
protected options: {
|
||||
env?: kx.types.Container['env'];
|
||||
password: string;
|
||||
}
|
||||
},
|
||||
) {}
|
||||
|
||||
deploy({ limits }: { limits: k8s.types.input.core.v1.ResourceRequirements['limits'] }) {
|
||||
|
|
@ -105,7 +105,7 @@ fi
|
|||
},
|
||||
{
|
||||
annotations: metadata.annotations,
|
||||
}
|
||||
},
|
||||
),
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ export class RemoteArtifactAsServiceDeployment {
|
|||
};
|
||||
},
|
||||
protected dependencies?: Array<pulumi.Resource | undefined | null>,
|
||||
protected parent?: pulumi.Resource | null
|
||||
protected parent?: pulumi.Resource | null,
|
||||
) {}
|
||||
|
||||
deployAsJob() {
|
||||
|
|
@ -50,7 +50,7 @@ export class RemoteArtifactAsServiceDeployment {
|
|||
{
|
||||
spec: pb.asJobSpec(),
|
||||
},
|
||||
{ dependsOn: this.dependencies?.filter(isDefined) }
|
||||
{ dependsOn: this.dependencies?.filter(isDefined) },
|
||||
);
|
||||
|
||||
return { job };
|
||||
|
|
@ -214,13 +214,13 @@ export class RemoteArtifactAsServiceDeployment {
|
|||
},
|
||||
{
|
||||
annotations: metadata.annotations,
|
||||
}
|
||||
},
|
||||
),
|
||||
},
|
||||
{
|
||||
dependsOn: this.dependencies?.filter(isDefined),
|
||||
parent: this.parent ?? undefined,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (this.options.pdb) {
|
||||
|
|
@ -265,7 +265,7 @@ export class RemoteArtifactAsServiceDeployment {
|
|||
},
|
||||
{
|
||||
dependsOn: [deployment, service],
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ export class Proxy {
|
|||
virtualHost?: Output<string>;
|
||||
httpsUpstream?: boolean;
|
||||
withWwwDomain?: boolean;
|
||||
}[]
|
||||
}[],
|
||||
) {
|
||||
const cert = new k8s.apiextensions.CustomResource(`cert-${dns.record}`, {
|
||||
apiVersion: 'cert-manager.io/v1',
|
||||
|
|
@ -104,7 +104,7 @@ export class Proxy {
|
|||
},
|
||||
{
|
||||
dependsOn: [cert, this.lbService!],
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return this;
|
||||
|
|
@ -183,7 +183,10 @@ export class Proxy {
|
|||
|
||||
this.lbService = proxyController.getResource('v1/Service', 'contour/contour-proxy-envoy');
|
||||
|
||||
const contourDeployment = proxyController.getResource('apps/v1/Deployment', 'contour/contour-proxy-contour');
|
||||
const contourDeployment = proxyController.getResource(
|
||||
'apps/v1/Deployment',
|
||||
'contour/contour-proxy-contour',
|
||||
);
|
||||
new k8s.policy.v1.PodDisruptionBudget('contour-pdb', {
|
||||
spec: {
|
||||
minAvailable: 1,
|
||||
|
|
@ -191,7 +194,10 @@ export class Proxy {
|
|||
},
|
||||
});
|
||||
|
||||
const envoyDaemonset = proxyController.getResource('apps/v1/ReplicaSet', 'contour/contour-proxy-envoy');
|
||||
const envoyDaemonset = proxyController.getResource(
|
||||
'apps/v1/ReplicaSet',
|
||||
'contour/contour-proxy-envoy',
|
||||
);
|
||||
new k8s.policy.v1.PodDisruptionBudget('envoy-pdb', {
|
||||
spec: {
|
||||
minAvailable: 1,
|
||||
|
|
@ -219,7 +225,7 @@ export class Proxy {
|
|||
},
|
||||
{
|
||||
dependsOn: [this.lbService],
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return this;
|
||||
|
|
|
|||
|
|
@ -65,7 +65,18 @@ services:
|
|||
soft: 20000
|
||||
hard: 40000
|
||||
healthcheck:
|
||||
test: ['CMD', 'cub', 'kafka-ready', '1', '5', '-b', '127.0.0.1:9092', '-c', '/etc/kafka/kafka.properties']
|
||||
test:
|
||||
[
|
||||
'CMD',
|
||||
'cub',
|
||||
'kafka-ready',
|
||||
'1',
|
||||
'5',
|
||||
'-b',
|
||||
'127.0.0.1:9092',
|
||||
'-c',
|
||||
'/etc/kafka/kafka.properties',
|
||||
]
|
||||
interval: 15s
|
||||
timeout: 10s
|
||||
retries: 6
|
||||
|
|
|
|||
|
|
@ -4,4 +4,5 @@
|
|||
|
||||

|
||||
|
||||
Note: The relationships in this diagram is based on [this docker-compose](https://github.com/kamilkisiela/graphql-hive/blob/main/docker-compose.community.yml)
|
||||
Note: The relationships in this diagram is based on
|
||||
[this docker-compose](https://github.com/kamilkisiela/graphql-hive/blob/main/docker-compose.community.yml)
|
||||
|
|
|
|||
|
|
@ -1,15 +1,23 @@
|
|||
## Deployment
|
||||
|
||||
Deployment is based on NPM packages. That means we are bundling (as much as possible) each service or package, and publish it to the private GitHub Packages artifactory.
|
||||
Deployment is based on NPM packages. That means we are bundling (as much as possible) each service
|
||||
or package, and publish it to the private GitHub Packages artifactory.
|
||||
|
||||
Doing that allows us to have a simple, super fast deployments, because we don't need to deal with Docker images (which are heavy).
|
||||
Doing that allows us to have a simple, super fast deployments, because we don't need to deal with
|
||||
Docker images (which are heavy).
|
||||
|
||||
We create an executable package (with `bin` entrypoint) and then use `npx PACKAGE_NAME@PACKAGE_VERSION` as command for a base Docker image of NodeJS. So instead of building a Docker image for each change, we build NPM package, and the Docker image we are using in prod is the same.
|
||||
We create an executable package (with `bin` entrypoint) and then use
|
||||
`npx PACKAGE_NAME@PACKAGE_VERSION` as command for a base Docker image of NodeJS. So instead of
|
||||
building a Docker image for each change, we build NPM package, and the Docker image we are using in
|
||||
prod is the same.
|
||||
|
||||
Think of it as Lambda (bundled JS, runtime is predefined) without all the crap (weird cache, weird pricing, cold start and so on).
|
||||
Think of it as Lambda (bundled JS, runtime is predefined) without all the crap (weird cache, weird
|
||||
pricing, cold start and so on).
|
||||
|
||||
### How to deploy?
|
||||
|
||||
We are using Pulumi (infrastructure as code) to describe and run our deployment. It's managed as GitHub Actions that runs on every bump release by Changesets.
|
||||
We are using Pulumi (infrastructure as code) to describe and run our deployment. It's managed as
|
||||
GitHub Actions that runs on every bump release by Changesets.
|
||||
|
||||
So changes are aggregated in a Changesets PR, and when merge, it updated the deployment manifest `package.json`, leading to a deployment of only the updated packages to production.
|
||||
So changes are aggregated in a Changesets PR, and when merge, it updated the deployment manifest
|
||||
`package.json`, leading to a deployment of only the updated packages to production.
|
||||
|
|
|
|||
|
|
@ -16,14 +16,18 @@ Developing Hive locally requires you to have the following software installed lo
|
|||
- In the root of the repo, run `nvm use` to use the same version of node as mentioned
|
||||
- Run `pnpm i` at the root to install all the dependencies and run the hooks
|
||||
- Run `pnpm setup` to create and apply migrations on the PostgreSQL database
|
||||
- Run `pnpm generate` to generate the typings from the graphql files (use `pnpm graphql:generate` if you only need to run GraphQL Codegen)
|
||||
- Run `pnpm generate` to generate the typings from the graphql files (use `pnpm graphql:generate` if
|
||||
you only need to run GraphQL Codegen)
|
||||
- Run `pnpm build` to build all services
|
||||
- Click on `Start Hive` in the bottom bar of VSCode
|
||||
- If you are not added to the list of guest users, request access from The Guild maintainers
|
||||
- Alternatively, [configure hive to use your own Auth0 Application](#setting-up-auth0-app-for-developing)
|
||||
- Alternatively,
|
||||
[configure hive to use your own Auth0 Application](#setting-up-auth0-app-for-developing)
|
||||
- Open the UI (`http://localhost:3000` by default) and Sign in with any of the identity provider
|
||||
- Once this is done, you should be able to login and use the project
|
||||
- Once you generate the token against your organization/personal account in hive, the same can be added locally to `hive.json` within `packages/libraries/cli` which can be used to interact via the hive cli with the registry
|
||||
- Once you generate the token against your organization/personal account in hive, the same can be
|
||||
added locally to `hive.json` within `packages/libraries/cli` which can be used to interact via the
|
||||
hive cli with the registry
|
||||
|
||||
## Development Seed
|
||||
|
||||
|
|
@ -33,11 +37,15 @@ We have a script to feed your local instance of Hive.
|
|||
2. Make sure `usage` and `usage-ingestor` are running as well (with `pnpm dev`)
|
||||
3. Open Hive app, create a project and a target, then create a token
|
||||
4. Run the seed script: `TOKEN="MY_TOKEN_HERE" pnpm seed`
|
||||
5. This should report a dummy schema and some dummy usage data to your local instance of Hive, allowing you to test features e2e
|
||||
5. This should report a dummy schema and some dummy usage data to your local instance of Hive,
|
||||
allowing you to test features e2e
|
||||
|
||||
> Note: You can set `STAGING=1` in order to target staging env and seed a target there.
|
||||
|
||||
> To send more operations and test heavy load on Hive instance, you can also set `OPERATIONS` (amount of operations in each interval round, default is `1`) and `INTERVAL` (frequency of sending operations, default: `1000`ms). For example, using `INTERVAL=1000 OPERATIONS=1000` will send 1000 requests per second.
|
||||
> To send more operations and test heavy load on Hive instance, you can also set `OPERATIONS`
|
||||
> (amount of operations in each interval round, default is `1`) and `INTERVAL` (frequency of sending
|
||||
> operations, default: `1000`ms). For example, using `INTERVAL=1000 OPERATIONS=1000` will send 1000
|
||||
> requests per second.
|
||||
|
||||
## Publish your first schema (manually)
|
||||
|
||||
|
|
@ -45,14 +53,18 @@ We have a script to feed your local instance of Hive.
|
|||
2. Create a project and a target
|
||||
3. Create a token from that target
|
||||
4. Go to `packages/libraries/cli` and run `pnpm build`
|
||||
5. Inside `packages/libraries/cli`, run: `pnpm start schema:publish --token "YOUR_TOKEN_HERE" --registry "http://localhost:4000/graphql" examples/single.graphql`
|
||||
5. Inside `packages/libraries/cli`, run:
|
||||
`pnpm start schema:publish --token "YOUR_TOKEN_HERE" --registry "http://localhost:4000/graphql" examples/single.graphql`
|
||||
|
||||
### Setting up Slack App for developing
|
||||
|
||||
1. [Download](https://loophole.cloud/download) Loophole CLI (same as ngrok but supports non-random urls)
|
||||
1. [Download](https://loophole.cloud/download) Loophole CLI (same as ngrok but supports non-random
|
||||
urls)
|
||||
2. Log in to Loophole `$ loophole account login`
|
||||
3. Start the proxy by running `$ loophole http 3000 --hostname hive-<your-name>` (@kamilkisiela I use `hive-kamil`). It creates `https://hive-<your-name>.loophole.site` endpoint.
|
||||
4. Message @kamilkisiela and send him the url (He will update the list of accepted redirect urls in both Auth0 and Slack App).
|
||||
3. Start the proxy by running `$ loophole http 3000 --hostname hive-<your-name>` (@kamilkisiela I
|
||||
use `hive-kamil`). It creates `https://hive-<your-name>.loophole.site` endpoint.
|
||||
4. Message @kamilkisiela and send him the url (He will update the list of accepted redirect urls in
|
||||
both Auth0 and Slack App).
|
||||
5. Update `APP_BASE_URL` and `AUTH0_BASE_URL` in [`packages/web/app/.env`](./packages/web/app/.env)
|
||||
6. Run `packages/web/app` and open `https://hive-<your-name>.loophole.site`.
|
||||
|
||||
|
|
@ -61,25 +73,28 @@ We have a script to feed your local instance of Hive.
|
|||
### Setting up GitHub App for developing
|
||||
|
||||
1. Follow the steps above for Slack App
|
||||
2. Update `Setup URL` in [GraphQL Hive Development](https://github.com/organizations/the-guild-org/settings/apps/graphql-hive-development) app and set it to `https://hive-<your-name>.loophole.site/api/github/setup-callback`
|
||||
2. Update `Setup URL` in
|
||||
[GraphQL Hive Development](https://github.com/organizations/the-guild-org/settings/apps/graphql-hive-development)
|
||||
app and set it to `https://hive-<your-name>.loophole.site/api/github/setup-callback`
|
||||
|
||||
### Run Hive
|
||||
|
||||
1. Click on Start Hive in the bottom bar of VSCode
|
||||
2. Open the UI (`http://localhost:3000` by default) and register any email and
|
||||
password
|
||||
3. Sending e-mails is mocked out during local development, so in order to
|
||||
verify the account find the verification link by visiting the email server's
|
||||
`/_history` endpoint - `http://localhost:6260/_history` by default.
|
||||
2. Open the UI (`http://localhost:3000` by default) and register any email and password
|
||||
3. Sending e-mails is mocked out during local development, so in order to verify the account find
|
||||
the verification link by visiting the email server's `/_history` endpoint -
|
||||
`http://localhost:6260/_history` by default.
|
||||
- Searching for `token` should help you find the link.
|
||||
|
||||
### Legacy Auth0 Integration
|
||||
|
||||
**Note:** If you are not working at The Guild, you can safely ignore this section.
|
||||
|
||||
Since we migrated from Auth0 to SuperTokens there is a compatibility layer for importing/migrating accounts from Auth0 to SuperTokens.
|
||||
Since we migrated from Auth0 to SuperTokens there is a compatibility layer for importing/migrating
|
||||
accounts from Auth0 to SuperTokens.
|
||||
|
||||
By default you don't need to set this up and can just use SuperTokens locally. However, if you need to test some stuff or fix the Auth0 -> SuperTokens migration flow you have to set up some stuff.
|
||||
By default you don't need to set this up and can just use SuperTokens locally. However, if you need
|
||||
to test some stuff or fix the Auth0 -> SuperTokens migration flow you have to set up some stuff.
|
||||
|
||||
1. Create your own Auth0 application
|
||||
1. If you haven't already, create an account on [manage.auth0.com](https://manage.auth0.com)
|
||||
|
|
@ -108,9 +123,11 @@ By default you don't need to set this up and can just use SuperTokens locally. H
|
|||
return callback(null, user, context);
|
||||
}
|
||||
```
|
||||
2. Update the `.env` secrets used by your local hive instance that are found when viewing your new application on Auth0:
|
||||
2. Update the `.env` secrets used by your local hive instance that are found when viewing your new
|
||||
application on Auth0:
|
||||
- `AUTH_LEGACY_AUTH0` (set this to `1` for enabling the migration.)
|
||||
- `AUTH_LEGACY_AUTH0_CLIENT_ID` (e.g. `rGSrExtM9sfilpF8kbMULkMNYI2SgXro`)
|
||||
- `AUTH_LEGACY_AUTH0_CLIENT_SECRET` (e.g. `gJjNQJsCaOC0nCKTgqWv2wvrh1XXXb-iqzVdn8pi2nSPq2TxxxJ9FIUYbNjheXxx`)
|
||||
- `AUTH_LEGACY_AUTH0_CLIENT_SECRET` (e.g.
|
||||
`gJjNQJsCaOC0nCKTgqWv2wvrh1XXXb-iqzVdn8pi2nSPq2TxxxJ9FIUYbNjheXxx`)
|
||||
- `AUTH_LEGACY_AUTH0_ISSUER_BASE_URL`(e.g. `https://foo-bars.us.auth0.com`)
|
||||
- `AUTH_LEGACY_AUTH0_AUDIENCE` (e.g. `https://foo-bars.us.auth0.com/api/v2/`)
|
||||
|
|
|
|||
|
|
@ -15,7 +15,8 @@ We are using Dockest to test the following concerns:
|
|||
|
||||
To run integration tests locally, follow:
|
||||
|
||||
1. Make sure you have Docker installed. If you are having issues, try to run `docker system prune` to clean the Docker caches.
|
||||
1. Make sure you have Docker installed. If you are having issues, try to run `docker system prune`
|
||||
to clean the Docker caches.
|
||||
2. Install all deps: `pnpm i`
|
||||
3. Generate types: `pnpm graphql:generate`
|
||||
4. Build and pack all services: `pnpm --filter integration-tests build-and-pack`
|
||||
|
|
|
|||
|
|
@ -70,7 +70,18 @@ services:
|
|||
soft: 20000
|
||||
hard: 40000
|
||||
healthcheck:
|
||||
test: ['CMD', 'cub', 'kafka-ready', '1', '5', '-b', '127.0.0.1:9092', '-c', '/etc/kafka/kafka.properties']
|
||||
test:
|
||||
[
|
||||
'CMD',
|
||||
'cub',
|
||||
'kafka-ready',
|
||||
'1',
|
||||
'5',
|
||||
'-b',
|
||||
'127.0.0.1:9092',
|
||||
'-c',
|
||||
'/etc/kafka/kafka.properties',
|
||||
]
|
||||
interval: 15s
|
||||
timeout: 10s
|
||||
retries: 6
|
||||
|
|
|
|||
|
|
@ -16,5 +16,5 @@ beforeEach(() =>
|
|||
host: redisAddress.replace(':6379', ''),
|
||||
port: 6379,
|
||||
password: 'test',
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,35 +1,35 @@
|
|||
{
|
||||
"name": "integration-tests",
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"build-and-pack": "(cd ../ && pnpm build:services && pnpm build:libraries && pnpm build:local) && node ./scripts/pack.mjs",
|
||||
"build:local": "pnpm build-and-pack && (cd ../ && pnpm docker:build)",
|
||||
"dockest": "tsup-node dockest.ts --format esm --target node16 && PWD=$PWD/.. node dist/dockest.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@app/gql": "link:./testkit/gql",
|
||||
"@graphql-hive/core": "0.2.3",
|
||||
"@graphql-typed-document-node/core": "3.1.1",
|
||||
"@n1ru4l/dockest": "2.1.0-rc.6",
|
||||
"@trpc/client": "9.23.2",
|
||||
"@whatwg-node/fetch": "0.4.7",
|
||||
"auth0": "2.36.2",
|
||||
"axios": "0.27.2",
|
||||
"dotenv": "10.0.0",
|
||||
"date-fns": "2.25.0",
|
||||
"dependency-graph": "0.11.0",
|
||||
"@n1ru4l/dockest": "2.1.0-rc.6",
|
||||
"dotenv": "10.0.0",
|
||||
"ioredis": "4.28.5",
|
||||
"rxjs": "^6.5.4",
|
||||
"slonik": "30.1.2",
|
||||
"tsup": "6.5.0",
|
||||
"yaml": "2.1.0",
|
||||
"@whatwg-node/fetch": "0.4.7",
|
||||
"zod": "3.15.1",
|
||||
"@trpc/client": "9.23.2"
|
||||
"zod": "3.15.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@hive/server": "workspace:*",
|
||||
"@types/ioredis": "4.28.10",
|
||||
"tslib": "2.4.1"
|
||||
},
|
||||
"scripts": {
|
||||
"build-and-pack": "(cd ../ && pnpm build:services && pnpm build:libraries && pnpm build:local) && node ./scripts/pack.mjs",
|
||||
"build:local": "pnpm build-and-pack && (cd ../ && pnpm docker:build)",
|
||||
"dockest": "tsup-node dockest.ts --format esm --target node16 && PWD=$PWD/.. node dist/dockest.js"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,9 @@ async function main() {
|
|||
fsExtra.mkdirSync(tarballDir, { recursive: true });
|
||||
|
||||
function isBackendPackage(manifestPath) {
|
||||
return JSON.parse(fs.readFileSync(manifestPath, 'utf-8')).buildOptions?.tags.includes('backend');
|
||||
return JSON.parse(fs.readFileSync(manifestPath, 'utf-8')).buildOptions?.tags.includes(
|
||||
'backend',
|
||||
);
|
||||
}
|
||||
|
||||
function listBackendPackages() {
|
||||
|
|
@ -35,11 +37,15 @@ async function main() {
|
|||
ignore: ['**/node_modules/**', '**/dist/**'],
|
||||
});
|
||||
|
||||
return manifestPathCollection.filter(isBackendPackage).map(filepath => path.relative(cwd, path.dirname(filepath)));
|
||||
return manifestPathCollection
|
||||
.filter(isBackendPackage)
|
||||
.map(filepath => path.relative(cwd, path.dirname(filepath)));
|
||||
}
|
||||
|
||||
async function pack(location) {
|
||||
const { version, name } = JSON.parse(await fsExtra.readFile(path.join(cwd, location, 'package.json'), 'utf-8'));
|
||||
const { version, name } = JSON.parse(
|
||||
await fsExtra.readFile(path.join(cwd, location, 'package.json'), 'utf-8'),
|
||||
);
|
||||
const stdout = await new Promise((resolve, reject) => {
|
||||
exec(
|
||||
`npm pack ${path.join(cwd, location, 'dist')}`,
|
||||
|
|
@ -54,7 +60,7 @@ async function main() {
|
|||
} else {
|
||||
resolve(stdout);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -86,7 +92,7 @@ async function main() {
|
|||
console.error('[pack] Maybe you forgot to build the packages first?');
|
||||
process.exit(1);
|
||||
}
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ const SignUpSignInUserResponseModel = z.object({
|
|||
|
||||
const signUpUserViaEmail = async (
|
||||
email: string,
|
||||
password: string
|
||||
password: string,
|
||||
): Promise<z.TypeOf<typeof SignUpSignInUserResponseModel>> => {
|
||||
const response = await fetch(`${ensureEnv('SUPERTOKENS_CONNECTION_URI')}/recipe/signup`, {
|
||||
method: 'POST',
|
||||
|
|
@ -63,7 +63,11 @@ const CreateSessionModel = z.object({
|
|||
}),
|
||||
});
|
||||
|
||||
const createSession = async (superTokensUserId: string, email: string, oidcIntegrationId: string | null) => {
|
||||
const createSession = async (
|
||||
superTokensUserId: string,
|
||||
email: string,
|
||||
oidcIntegrationId: string | null,
|
||||
) => {
|
||||
await internalApi.mutation('ensureUser', {
|
||||
superTokensUserId,
|
||||
email,
|
||||
|
|
@ -111,18 +115,27 @@ export const userEmails: Record<UserID, string> = {
|
|||
admin: 'admin@localhost.localhost',
|
||||
};
|
||||
|
||||
const tokenResponsePromise: Record<UserID, Promise<z.TypeOf<typeof SignUpSignInUserResponseModel>> | null> = {
|
||||
const tokenResponsePromise: Record<
|
||||
UserID,
|
||||
Promise<z.TypeOf<typeof SignUpSignInUserResponseModel>> | null
|
||||
> = {
|
||||
main: null,
|
||||
extra: null,
|
||||
admin: null,
|
||||
};
|
||||
|
||||
export function authenticate(userId: UserID, oidcIntegrationId?: string): Promise<{ access_token: string }> {
|
||||
export function authenticate(
|
||||
userId: UserID,
|
||||
oidcIntegrationId?: string,
|
||||
): Promise<{ access_token: string }> {
|
||||
if (!tokenResponsePromise[userId]) {
|
||||
tokenResponsePromise[userId] = signUpUserViaEmail(userEmails[userId] ?? `${userId}@localhost.localhost`, password);
|
||||
tokenResponsePromise[userId] = signUpUserViaEmail(
|
||||
userEmails[userId] ?? `${userId}@localhost.localhost`,
|
||||
password,
|
||||
);
|
||||
}
|
||||
|
||||
return tokenResponsePromise[userId]!.then(data =>
|
||||
createSession(data.user.id, data.user.email, oidcIntegrationId ?? null)
|
||||
createSession(data.user.id, data.user.email, oidcIntegrationId ?? null),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,9 +16,13 @@ async function exec(cmd: string) {
|
|||
}
|
||||
|
||||
export async function schemaPublish(args: string[]) {
|
||||
return exec(['schema:publish', `--registry`, `http://${registryAddress}/graphql`, ...args].join(' '));
|
||||
return exec(
|
||||
['schema:publish', `--registry`, `http://${registryAddress}/graphql`, ...args].join(' '),
|
||||
);
|
||||
}
|
||||
|
||||
export async function schemaCheck(args: string[]) {
|
||||
return exec(['schema:check', `--registry`, `http://${registryAddress}/graphql`, ...args].join(' '));
|
||||
return exec(
|
||||
['schema:check', `--registry`, `http://${registryAddress}/graphql`, ...args].join(' '),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,14 +9,16 @@ export const resetDb = async (conn: DatabasePoolConnection) => {
|
|||
WHERE "schemaname" = 'public';
|
||||
`);
|
||||
|
||||
const tablenames = result.map(({ tablename }) => tablename).filter(tablename => !migrationTables.includes(tablename));
|
||||
const tablenames = result
|
||||
.map(({ tablename }) => tablename)
|
||||
.filter(tablename => !migrationTables.includes(tablename));
|
||||
|
||||
if (tablenames.length) {
|
||||
await conn.query(sql`
|
||||
TRUNCATE TABLE
|
||||
${sql.join(
|
||||
tablenames.map(name => sql.identifier([name])),
|
||||
sql`,`
|
||||
sql`,`,
|
||||
)}
|
||||
RESTART IDENTITY
|
||||
;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ export const startReadinessCheck: ReadinessCheck = ({ runner }) => {
|
|||
return runner.dockerEventStream$.pipe(
|
||||
filter(ev => ev.action === 'start'),
|
||||
mapTo(undefined),
|
||||
take(1)
|
||||
take(1),
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -82,7 +82,9 @@ export function createServices() {
|
|||
}
|
||||
|
||||
export function cleanDockerContainers() {
|
||||
const output = execa(`docker ps --all --filter "name=integration-tests" --format={{.ID}}:{{.Status}}`);
|
||||
const output = execa(
|
||||
`docker ps --all --filter "name=integration-tests" --format={{.ID}}:{{.Status}}`,
|
||||
);
|
||||
|
||||
if (output.stdout.length) {
|
||||
const runningContainers = output.stdout.split('\n');
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ export function invariant(
|
|||
condition: any,
|
||||
// Can provide a string, or a function that returns a string for cases where
|
||||
// the message takes a fair amount of effort to compute
|
||||
message?: string | (() => string)
|
||||
message?: string | (() => string),
|
||||
): asserts condition {
|
||||
if (condition) {
|
||||
return;
|
||||
|
|
@ -32,6 +32,7 @@ export function ensureEnv(key: string, valueType: 'string'): string;
|
|||
export function ensureEnv(key: string, valueType: 'number'): number;
|
||||
export function ensureEnv(key: string, valueType: 'boolean'): boolean;
|
||||
export function ensureEnv(key: string, valueType?: ValueType) {
|
||||
// eslint-disable-next-line no-process-env
|
||||
let value = process.env[key];
|
||||
|
||||
if (value === '<sync>') {
|
||||
|
|
|
|||
|
|
@ -393,7 +393,11 @@ export function updateMemberAccess(input: OrganizationMemberAccessInput, authTok
|
|||
});
|
||||
}
|
||||
|
||||
export function publishSchema(input: SchemaPublishInput, token: string, authHeader?: 'x-api-token' | 'authorization') {
|
||||
export function publishSchema(
|
||||
input: SchemaPublishInput,
|
||||
token: string,
|
||||
authHeader?: 'x-api-token' | 'authorization',
|
||||
) {
|
||||
return execute({
|
||||
document: gql(/* GraphQL */ `
|
||||
mutation schemaPublish($input: SchemaPublishInput!) {
|
||||
|
|
@ -491,7 +495,7 @@ export function setTargetValidation(
|
|||
}
|
||||
| {
|
||||
authToken: string;
|
||||
}
|
||||
},
|
||||
) {
|
||||
return execute({
|
||||
document: gql(/* GraphQL */ `
|
||||
|
|
@ -519,7 +523,7 @@ export function updateTargetValidationSettings(
|
|||
}
|
||||
| {
|
||||
authToken: string;
|
||||
}
|
||||
},
|
||||
) {
|
||||
return execute({
|
||||
document: gql(/* GraphQL */ `
|
||||
|
|
@ -838,11 +842,14 @@ export async function fetchMetadataFromCDN(selector: TargetSelectorInput, token:
|
|||
export async function updateOrgRateLimit(
|
||||
selector: OrganizationSelectorInput,
|
||||
monthlyLimits: RateLimitInput,
|
||||
authToken: string
|
||||
authToken: string,
|
||||
) {
|
||||
return execute({
|
||||
document: gql(/* GraphQL */ `
|
||||
mutation updateOrgRateLimit($selector: OrganizationSelectorInput!, $monthlyLimits: RateLimitInput!) {
|
||||
mutation updateOrgRateLimit(
|
||||
$selector: OrganizationSelectorInput!
|
||||
$monthlyLimits: RateLimitInput!
|
||||
) {
|
||||
updateOrgRateLimit(selector: $selector, monthlyLimits: $monthlyLimits) {
|
||||
id
|
||||
}
|
||||
|
|
@ -856,7 +863,10 @@ export async function updateOrgRateLimit(
|
|||
});
|
||||
}
|
||||
|
||||
export async function enableExternalSchemaComposition(input: EnableExternalSchemaCompositionInput, token: string) {
|
||||
export async function enableExternalSchemaComposition(
|
||||
input: EnableExternalSchemaCompositionInput,
|
||||
token: string,
|
||||
) {
|
||||
return execute({
|
||||
document: gql(/* GraphQL */ `
|
||||
mutation enableExternalSchemaComposition($input: EnableExternalSchemaCompositionInput!) {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,9 @@ export async function execute<TResult, TVariables>(
|
|||
authToken?: string;
|
||||
token?: string;
|
||||
legacyAuthorizationMode?: boolean;
|
||||
} & (TVariables extends Record<string, never> ? { variables?: never } : { variables: TVariables })
|
||||
} & (TVariables extends Record<string, never>
|
||||
? { variables?: never }
|
||||
: { variables: TVariables }),
|
||||
) {
|
||||
const response = await fetch(`http://${registryAddress}/graphql`, {
|
||||
method: 'POST',
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ test('X-API-Token header should work when calling GraphQL API and collecting usa
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
|
@ -29,7 +29,7 @@ test('X-API-Token header should work when calling GraphQL API and collecting usa
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -45,7 +45,7 @@ test('X-API-Token header should work when calling GraphQL API and collecting usa
|
|||
projectScopes: [],
|
||||
targetScopes: [TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
expect(tokenResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -59,7 +59,7 @@ test('X-API-Token header should work when calling GraphQL API and collecting usa
|
|||
sdl: `type Query { ping: String }`,
|
||||
},
|
||||
token,
|
||||
'x-api-token'
|
||||
'x-api-token',
|
||||
);
|
||||
|
||||
expect(result.body.errors).not.toBeDefined();
|
||||
|
|
@ -98,7 +98,7 @@ test('X-API-Token header should work when calling GraphQL API and collecting usa
|
|||
to,
|
||||
},
|
||||
},
|
||||
token
|
||||
token,
|
||||
);
|
||||
|
||||
expect(operationStatsResult.body.errors).not.toBeDefined();
|
||||
|
|
|
|||
|
|
@ -47,10 +47,11 @@ describe('create', () => {
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
access_token
|
||||
access_token,
|
||||
);
|
||||
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
const org =
|
||||
orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
||||
const result = await execute({
|
||||
document: CreateOIDCIntegrationMutation,
|
||||
|
|
@ -139,10 +140,11 @@ describe('create', () => {
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
access_token
|
||||
access_token,
|
||||
);
|
||||
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
const org =
|
||||
orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
||||
const result = await execute({
|
||||
document: CreateOIDCIntegrationMutation,
|
||||
|
|
@ -180,10 +182,11 @@ describe('create', () => {
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
access_token
|
||||
access_token,
|
||||
);
|
||||
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
const org =
|
||||
orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
||||
const result = await execute({
|
||||
document: CreateOIDCIntegrationMutation,
|
||||
|
|
@ -221,10 +224,11 @@ describe('create', () => {
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
access_token
|
||||
access_token,
|
||||
);
|
||||
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
const org =
|
||||
orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
||||
const result = await execute({
|
||||
document: CreateOIDCIntegrationMutation,
|
||||
|
|
@ -262,10 +266,11 @@ describe('create', () => {
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
access_token
|
||||
access_token,
|
||||
);
|
||||
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
const org =
|
||||
orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
||||
const result = await execute({
|
||||
document: CreateOIDCIntegrationMutation,
|
||||
|
|
@ -303,10 +308,11 @@ describe('create', () => {
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
access_token
|
||||
access_token,
|
||||
);
|
||||
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
const org =
|
||||
orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
||||
const result = await execute({
|
||||
document: CreateOIDCIntegrationMutation,
|
||||
|
|
@ -344,10 +350,11 @@ describe('create', () => {
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
access_token
|
||||
access_token,
|
||||
);
|
||||
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
const org =
|
||||
orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
||||
let result = await execute({
|
||||
document: CreateOIDCIntegrationMutation,
|
||||
|
|
@ -429,10 +436,11 @@ describe('delete', () => {
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
access_token
|
||||
access_token,
|
||||
);
|
||||
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
const org =
|
||||
orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
||||
const createResult = await execute({
|
||||
document: CreateOIDCIntegrationMutation,
|
||||
|
|
@ -448,7 +456,8 @@ describe('delete', () => {
|
|||
});
|
||||
|
||||
expect(createResult.body.errors).toBeUndefined();
|
||||
const oidcIntegrationId = createResult.body.data!.createOIDCIntegration.ok!.createdOIDCIntegration.id;
|
||||
const oidcIntegrationId =
|
||||
createResult.body.data!.createOIDCIntegration.ok!.createdOIDCIntegration.id;
|
||||
|
||||
let refetchedOrg = await execute({
|
||||
document: OrganizationWithOIDCIntegration,
|
||||
|
|
@ -522,10 +531,11 @@ describe('delete', () => {
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
access_token
|
||||
access_token,
|
||||
);
|
||||
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
const org =
|
||||
orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
||||
const createResult = await execute({
|
||||
document: CreateOIDCIntegrationMutation,
|
||||
|
|
@ -541,7 +551,8 @@ describe('delete', () => {
|
|||
});
|
||||
|
||||
expect(createResult.body.errors).toBeUndefined();
|
||||
const oidcIntegrationId = createResult.body.data!.createOIDCIntegration.ok!.createdOIDCIntegration.id;
|
||||
const oidcIntegrationId =
|
||||
createResult.body.data!.createOIDCIntegration.ok!.createdOIDCIntegration.id;
|
||||
|
||||
const { access_token: accessTokenExtra } = await authenticate('extra');
|
||||
|
||||
|
|
@ -580,10 +591,11 @@ describe('delete', () => {
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
access_token
|
||||
access_token,
|
||||
);
|
||||
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
const org =
|
||||
orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
||||
const createResult = await execute({
|
||||
document: CreateOIDCIntegrationMutation,
|
||||
|
|
@ -599,7 +611,8 @@ describe('delete', () => {
|
|||
});
|
||||
|
||||
expect(createResult.body.errors).toBeUndefined();
|
||||
const oidcIntegrationId = createResult.body.data!.createOIDCIntegration.ok!.createdOIDCIntegration.id;
|
||||
const oidcIntegrationId =
|
||||
createResult.body.data!.createOIDCIntegration.ok!.createdOIDCIntegration.id;
|
||||
|
||||
const MeQuery = gql(/* GraphQL */ `
|
||||
query Me {
|
||||
|
|
@ -610,7 +623,10 @@ describe('delete', () => {
|
|||
`);
|
||||
|
||||
// create new member that belongs to oidc integration
|
||||
const { access_token: memberAccessToken } = await authenticate('oidc_member', oidcIntegrationId);
|
||||
const { access_token: memberAccessToken } = await authenticate(
|
||||
'oidc_member',
|
||||
oidcIntegrationId,
|
||||
);
|
||||
let meResult = await execute({
|
||||
document: MeQuery,
|
||||
authToken: memberAccessToken,
|
||||
|
|
@ -694,10 +710,11 @@ describe('update', () => {
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
access_token
|
||||
access_token,
|
||||
);
|
||||
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
const org =
|
||||
orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
||||
const createResult = await execute({
|
||||
document: CreateOIDCIntegrationMutation,
|
||||
|
|
@ -713,7 +730,8 @@ describe('update', () => {
|
|||
});
|
||||
expect(createResult.body.errors).toBeUndefined();
|
||||
|
||||
const oidcIntegrationId = createResult.body.data!.createOIDCIntegration.ok!.createdOIDCIntegration.id;
|
||||
const oidcIntegrationId =
|
||||
createResult.body.data!.createOIDCIntegration.ok!.createdOIDCIntegration.id;
|
||||
|
||||
const updateResult = await execute({
|
||||
document: UpdateOIDCIntegrationMutation,
|
||||
|
|
@ -749,10 +767,11 @@ describe('update', () => {
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
access_token
|
||||
access_token,
|
||||
);
|
||||
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
const org =
|
||||
orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
||||
const createResult = await execute({
|
||||
document: CreateOIDCIntegrationMutation,
|
||||
|
|
@ -768,7 +787,8 @@ describe('update', () => {
|
|||
});
|
||||
|
||||
expect(createResult.body.errors).toBeUndefined();
|
||||
const oidcIntegrationId = createResult.body.data!.createOIDCIntegration.ok!.createdOIDCIntegration.id;
|
||||
const oidcIntegrationId =
|
||||
createResult.body.data!.createOIDCIntegration.ok!.createdOIDCIntegration.id;
|
||||
|
||||
const { access_token: accessTokenExtra } = await authenticate('extra');
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ test('renaming an organization should result changing its cleanId', async () =>
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
access_token
|
||||
access_token,
|
||||
);
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
||||
|
|
@ -16,19 +16,22 @@ test('renaming an organization should result changing its cleanId', async () =>
|
|||
organization: org.cleanId,
|
||||
name: 'bar',
|
||||
},
|
||||
access_token
|
||||
access_token,
|
||||
);
|
||||
|
||||
expect(renamedOrganizationResult.body.errors).not.toBeDefined();
|
||||
|
||||
expect(renamedOrganizationResult.body.data?.updateOrganizationName.error).toBeNull();
|
||||
expect(
|
||||
renamedOrganizationResult.body.data?.updateOrganizationName.ok?.updatedOrganizationPayload.organization.name
|
||||
renamedOrganizationResult.body.data?.updateOrganizationName.ok?.updatedOrganizationPayload
|
||||
.organization.name,
|
||||
).toBe('bar');
|
||||
expect(
|
||||
renamedOrganizationResult.body.data?.updateOrganizationName.ok?.updatedOrganizationPayload.organization.cleanId
|
||||
renamedOrganizationResult.body.data?.updateOrganizationName.ok?.updatedOrganizationPayload
|
||||
.organization.cleanId,
|
||||
).toBe('bar');
|
||||
expect(
|
||||
renamedOrganizationResult.body.data?.updateOrganizationName.ok?.updatedOrganizationPayload.selector.organization
|
||||
renamedOrganizationResult.body.data?.updateOrganizationName.ok?.updatedOrganizationPayload
|
||||
.selector.organization,
|
||||
).toBe('bar');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -12,7 +12,12 @@ import {
|
|||
} from '../../../testkit/flow';
|
||||
import { authenticate } from '../../../testkit/auth';
|
||||
import { collect } from '../../../testkit/usage';
|
||||
import { TargetAccessScope, ProjectType, ProjectAccessScope, OrganizationAccessScope } from '@app/gql/graphql';
|
||||
import {
|
||||
TargetAccessScope,
|
||||
ProjectType,
|
||||
ProjectAccessScope,
|
||||
OrganizationAccessScope,
|
||||
} from '@app/gql/graphql';
|
||||
|
||||
async function getSteps({ organization, token }: { organization: string; token: string }) {
|
||||
const result = await getOrganization(organization, token);
|
||||
|
|
@ -28,7 +33,7 @@ test('freshly created organization has Get Started progress at 0%', async () =>
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
access_token
|
||||
access_token,
|
||||
);
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
||||
|
|
@ -51,7 +56,7 @@ test('completing each step should result in updated Get Started progress', async
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
access_token
|
||||
access_token,
|
||||
);
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
||||
|
|
@ -63,7 +68,7 @@ test('completing each step should result in updated Get Started progress', async
|
|||
name: 'foo',
|
||||
type: ProjectType.Single,
|
||||
},
|
||||
access_token
|
||||
access_token,
|
||||
);
|
||||
|
||||
let steps = await getSteps({
|
||||
|
|
@ -80,7 +85,9 @@ test('completing each step should result in updated Get Started progress', async
|
|||
|
||||
expect(projectResult.body.errors).not.toBeDefined();
|
||||
|
||||
const target = projectResult.body.data?.createProject.ok?.createdTargets.find(t => t.name === 'production');
|
||||
const target = projectResult.body.data?.createProject.ok?.createdTargets.find(
|
||||
t => t.name === 'production',
|
||||
);
|
||||
const project = projectResult.body.data?.createProject.ok?.createdProject;
|
||||
|
||||
if (!target || !project) {
|
||||
|
|
@ -102,7 +109,7 @@ test('completing each step should result in updated Get Started progress', async
|
|||
TargetAccessScope.Settings,
|
||||
],
|
||||
},
|
||||
access_token
|
||||
access_token,
|
||||
);
|
||||
|
||||
expect(tokenResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -117,7 +124,7 @@ test('completing each step should result in updated Get Started progress', async
|
|||
commit: 'test',
|
||||
sdl: 'type Query { foo: String }',
|
||||
},
|
||||
token
|
||||
token,
|
||||
);
|
||||
|
||||
steps = await getSteps({
|
||||
|
|
@ -138,7 +145,7 @@ test('completing each step should result in updated Get Started progress', async
|
|||
{
|
||||
sdl: 'type Query { foo: String bar: String }',
|
||||
},
|
||||
token
|
||||
token,
|
||||
);
|
||||
|
||||
steps = await getSteps({
|
||||
|
|
@ -160,7 +167,7 @@ test('completing each step should result in updated Get Started progress', async
|
|||
email: 'some@email.com',
|
||||
organization: org.cleanId,
|
||||
},
|
||||
access_token
|
||||
access_token,
|
||||
);
|
||||
|
||||
const inviteCode = invitationResult.body.data?.inviteToOrganizationByEmail.ok?.code;
|
||||
|
|
@ -224,7 +231,7 @@ test('completing each step should result in updated Get Started progress', async
|
|||
},
|
||||
{
|
||||
token,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
steps = await getSteps({
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
import { OrganizationAccessScope, ProjectAccessScope, TargetAccessScope } from '@app/gql/graphql';
|
||||
import { createOrganization, inviteToOrganization, joinOrganization, updateMemberAccess } from '../../../testkit/flow';
|
||||
import {
|
||||
createOrganization,
|
||||
inviteToOrganization,
|
||||
joinOrganization,
|
||||
updateMemberAccess,
|
||||
} from '../../../testkit/flow';
|
||||
import { authenticate } from '../../../testkit/auth';
|
||||
import { history } from '../../../testkit/emails';
|
||||
|
||||
|
|
@ -9,12 +14,13 @@ test('owner of an organization should have all scopes', async () => {
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
access_token
|
||||
access_token,
|
||||
);
|
||||
|
||||
expect(result.body.errors).not.toBeDefined();
|
||||
|
||||
const owner = result.body.data!.createOrganization.ok!.createdOrganizationPayload.organization.owner;
|
||||
const owner =
|
||||
result.body.data!.createOrganization.ok!.createdOrganizationPayload.organization.owner;
|
||||
|
||||
Object.values(OrganizationAccessScope).forEach(scope => {
|
||||
expect(owner.organizationAccessScopes).toContain(scope);
|
||||
|
|
@ -35,7 +41,7 @@ test('regular member of an organization should have basic scopes', async () => {
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
|
@ -46,7 +52,7 @@ test('regular member of an organization should have basic scopes', async () => {
|
|||
email: 'some@email.com',
|
||||
organization: org.cleanId,
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const inviteCode = invitationResult.body.data?.inviteToOrganizationByEmail.ok?.code;
|
||||
|
|
@ -89,7 +95,7 @@ test('cannot grant an access scope to another user if user has no access to that
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
|
@ -100,7 +106,7 @@ test('cannot grant an access scope to another user if user has no access to that
|
|||
email: 'some@email.com',
|
||||
organization: org.cleanId,
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const inviteCode = invitationResult.body.data?.inviteToOrganizationByEmail.ok?.code;
|
||||
|
|
@ -125,7 +131,7 @@ test('cannot grant an access scope to another user if user has no access to that
|
|||
targetScopes: [],
|
||||
user: member.id,
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
// Grant access to target:tokens:write
|
||||
|
|
@ -137,7 +143,7 @@ test('cannot grant an access scope to another user if user has no access to that
|
|||
targetScopes: [TargetAccessScope.TokensWrite],
|
||||
user: member.id,
|
||||
},
|
||||
member_access_token
|
||||
member_access_token,
|
||||
);
|
||||
|
||||
expect(accessResult.body.errors).toHaveLength(1);
|
||||
|
|
@ -150,7 +156,7 @@ test('granting no scopes is equal to setting read-only for org, project and targ
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
|
@ -161,7 +167,7 @@ test('granting no scopes is equal to setting read-only for org, project and targ
|
|||
email: 'some@email.com',
|
||||
organization: org.cleanId,
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const inviteCode = invitationResult.body.data?.inviteToOrganizationByEmail.ok?.code;
|
||||
|
|
@ -186,14 +192,15 @@ test('granting no scopes is equal to setting read-only for org, project and targ
|
|||
targetScopes: [],
|
||||
user: member.id,
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
expect(accessResult.body.errors).not.toBeDefined();
|
||||
|
||||
const memberWithAccess = accessResult.body.data?.updateOrganizationMemberAccess.organization.members.nodes.find(
|
||||
m => m.id === member.id
|
||||
);
|
||||
const memberWithAccess =
|
||||
accessResult.body.data?.updateOrganizationMemberAccess.organization.members.nodes.find(
|
||||
m => m.id === member.id,
|
||||
);
|
||||
expect(memberWithAccess?.organizationAccessScopes).toHaveLength(1);
|
||||
expect(memberWithAccess?.organizationAccessScopes).toContainEqual(OrganizationAccessScope.Read);
|
||||
expect(memberWithAccess?.projectAccessScopes).toHaveLength(1);
|
||||
|
|
@ -208,9 +215,10 @@ test('email invitation', async () => {
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
const org = createOrgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
const org =
|
||||
createOrgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
||||
// Invite
|
||||
const email = 'invited@invited.com';
|
||||
|
|
@ -219,7 +227,7 @@ test('email invitation', async () => {
|
|||
email,
|
||||
organization: org.cleanId,
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const inviteCode = invitationResult.body.data?.inviteToOrganizationByEmail.ok?.code;
|
||||
|
|
@ -235,9 +243,10 @@ test('cannot join organization twice using the same invitation code', async () =
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
const org = createOrgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
const org =
|
||||
createOrgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
||||
// Invite
|
||||
const invitationResult = await inviteToOrganization(
|
||||
|
|
@ -245,7 +254,7 @@ test('cannot join organization twice using the same invitation code', async () =
|
|||
email: 'some@email.com',
|
||||
organization: org.cleanId,
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const inviteCode = invitationResult.body.data?.inviteToOrganizationByEmail.ok?.code;
|
||||
|
|
@ -264,5 +273,7 @@ test('cannot join organization twice using the same invitation code', async () =
|
|||
|
||||
const { access_token: another_access_token } = await authenticate('admin');
|
||||
const secondJoinResult = await joinOrganization(inviteCode!, another_access_token);
|
||||
expect(secondJoinResult.body.data?.joinOrganization.__typename).toBe('OrganizationInvitationError');
|
||||
expect(secondJoinResult.body.data?.joinOrganization.__typename).toBe(
|
||||
'OrganizationInvitationError',
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
import { ProjectType, ProjectAccessScope } from '@app/gql/graphql';
|
||||
import { createOrganization, publishPersistedOperations, createProject, createToken } from '../../../testkit/flow';
|
||||
import {
|
||||
createOrganization,
|
||||
publishPersistedOperations,
|
||||
createProject,
|
||||
createToken,
|
||||
} from '../../../testkit/flow';
|
||||
import { authenticate } from '../../../testkit/auth';
|
||||
|
||||
test('can publish persisted operations only with project:operations-store:write', async () => {
|
||||
|
|
@ -8,7 +13,7 @@ test('can publish persisted operations only with project:operations-store:write'
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
||||
|
|
@ -18,7 +23,7 @@ test('can publish persisted operations only with project:operations-store:write'
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -35,7 +40,7 @@ test('can publish persisted operations only with project:operations-store:write'
|
|||
projectScopes: [],
|
||||
targetScopes: [],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
expect(noAccessTokenResult.body.errors).not.toBeDefined();
|
||||
|
||||
|
|
@ -50,7 +55,7 @@ test('can publish persisted operations only with project:operations-store:write'
|
|||
projectScopes: [ProjectAccessScope.OperationsStoreRead],
|
||||
targetScopes: [],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
expect(readTokenResult.body.errors).not.toBeDefined();
|
||||
|
||||
|
|
@ -62,10 +67,13 @@ test('can publish persisted operations only with project:operations-store:write'
|
|||
project: project.cleanId,
|
||||
target: target.cleanId,
|
||||
organizationScopes: [],
|
||||
projectScopes: [ProjectAccessScope.OperationsStoreRead, ProjectAccessScope.OperationsStoreWrite],
|
||||
projectScopes: [
|
||||
ProjectAccessScope.OperationsStoreRead,
|
||||
ProjectAccessScope.OperationsStoreWrite,
|
||||
],
|
||||
targetScopes: [],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
expect(writeTokenResult.body.errors).not.toBeDefined();
|
||||
|
||||
|
|
@ -113,7 +121,7 @@ test('should skip on already persisted operations', async () => {
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
||||
|
|
@ -123,7 +131,7 @@ test('should skip on already persisted operations', async () => {
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -137,10 +145,13 @@ test('should skip on already persisted operations', async () => {
|
|||
project: project.cleanId,
|
||||
target: target.cleanId,
|
||||
organizationScopes: [],
|
||||
projectScopes: [ProjectAccessScope.OperationsStoreRead, ProjectAccessScope.OperationsStoreWrite],
|
||||
projectScopes: [
|
||||
ProjectAccessScope.OperationsStoreRead,
|
||||
ProjectAccessScope.OperationsStoreWrite,
|
||||
],
|
||||
targetScopes: [],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
expect(writeTokenResult.body.errors).not.toBeDefined();
|
||||
|
||||
|
|
@ -181,8 +192,12 @@ test('should skip on already persisted operations', async () => {
|
|||
expect(persisted.summary.unchanged).toEqual(1);
|
||||
expect(persisted.operations).toHaveLength(2);
|
||||
|
||||
const meOperation = persisted.operations.find(op => op.operationHash === operations[0].operationHash);
|
||||
const userOperation = persisted.operations.find(op => op.operationHash === operations[1].operationHash);
|
||||
const meOperation = persisted.operations.find(
|
||||
op => op.operationHash === operations[0].operationHash,
|
||||
);
|
||||
const userOperation = persisted.operations.find(
|
||||
op => op.operationHash === operations[1].operationHash,
|
||||
);
|
||||
|
||||
expect(meOperation?.operationHash).toEqual(operations[0].operationHash);
|
||||
expect(userOperation?.operationHash).toEqual(operations[1].operationHash);
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ test('creating a project should result in creating the development, staging and
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
access_token
|
||||
access_token,
|
||||
);
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
||||
|
|
@ -18,7 +18,7 @@ test('creating a project should result in creating the development, staging and
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
access_token
|
||||
access_token,
|
||||
);
|
||||
|
||||
const targets = projectResult.body.data!.createProject.ok!.createdTargets;
|
||||
|
|
@ -28,19 +28,19 @@ test('creating a project should result in creating the development, staging and
|
|||
expect.objectContaining({
|
||||
cleanId: 'development',
|
||||
name: 'development',
|
||||
})
|
||||
}),
|
||||
);
|
||||
expect(targets).toContainEqual(
|
||||
expect.objectContaining({
|
||||
cleanId: 'staging',
|
||||
name: 'staging',
|
||||
})
|
||||
}),
|
||||
);
|
||||
expect(targets).toContainEqual(
|
||||
expect.objectContaining({
|
||||
cleanId: 'production',
|
||||
name: 'production',
|
||||
})
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -50,7 +50,7 @@ test('renaming a project should result changing its cleanId', async () => {
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
access_token
|
||||
access_token,
|
||||
);
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
||||
|
|
@ -60,7 +60,7 @@ test('renaming a project should result changing its cleanId', async () => {
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
access_token
|
||||
access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -71,7 +71,7 @@ test('renaming a project should result changing its cleanId', async () => {
|
|||
project: project.cleanId,
|
||||
name: 'bar',
|
||||
},
|
||||
access_token
|
||||
access_token,
|
||||
);
|
||||
|
||||
expect(renamedProjectResult.body.errors).not.toBeDefined();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,16 @@
|
|||
import { TargetAccessScope, ProjectType, ProjectAccessScope, OrganizationAccessScope } from '@app/gql/graphql';
|
||||
import { createOrganization, createProject, createToken, updateOrgRateLimit, waitFor } from '../../../testkit/flow';
|
||||
import {
|
||||
TargetAccessScope,
|
||||
ProjectType,
|
||||
ProjectAccessScope,
|
||||
OrganizationAccessScope,
|
||||
} from '@app/gql/graphql';
|
||||
import {
|
||||
createOrganization,
|
||||
createProject,
|
||||
createToken,
|
||||
updateOrgRateLimit,
|
||||
waitFor,
|
||||
} from '../../../testkit/flow';
|
||||
import * as emails from '../../../testkit/emails';
|
||||
import { authenticate, userEmails } from '../../../testkit/auth';
|
||||
import { collect } from '../../../testkit/usage';
|
||||
|
|
@ -24,7 +35,7 @@ test('rate limit approaching and reached for organization', async () => {
|
|||
{
|
||||
name: generateUnique(),
|
||||
},
|
||||
access_token
|
||||
access_token,
|
||||
);
|
||||
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
|
@ -34,11 +45,13 @@ test('rate limit approaching and reached for organization', async () => {
|
|||
type: ProjectType.Single,
|
||||
name: 'bar',
|
||||
},
|
||||
access_token
|
||||
access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
const target = projectResult.body.data!.createProject.ok!.createdTargets.find(t => t.name === 'production')!;
|
||||
const target = projectResult.body.data!.createProject.ok!.createdTargets.find(
|
||||
t => t.name === 'production',
|
||||
)!;
|
||||
|
||||
await updateOrgRateLimit(
|
||||
{
|
||||
|
|
@ -47,7 +60,7 @@ test('rate limit approaching and reached for organization', async () => {
|
|||
{
|
||||
operations: 11,
|
||||
},
|
||||
access_token
|
||||
access_token,
|
||||
);
|
||||
|
||||
const tokenResult = await createToken(
|
||||
|
|
@ -58,9 +71,13 @@ test('rate limit approaching and reached for organization', async () => {
|
|||
target: target.cleanId,
|
||||
organizationScopes: [OrganizationAccessScope.Read],
|
||||
projectScopes: [ProjectAccessScope.Read],
|
||||
targetScopes: [TargetAccessScope.Read, TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
targetScopes: [
|
||||
TargetAccessScope.Read,
|
||||
TargetAccessScope.RegistryRead,
|
||||
TargetAccessScope.RegistryWrite,
|
||||
],
|
||||
},
|
||||
access_token
|
||||
access_token,
|
||||
);
|
||||
|
||||
expect(tokenResult.body.errors).not.toBeDefined();
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ test('can check a schema with target:registry:read access', async () => {
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
||||
|
|
@ -25,7 +25,7 @@ test('can check a schema with target:registry:read access', async () => {
|
|||
email: 'some@email.com',
|
||||
organization: org.cleanId,
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const inviteCode = invitationResult.body.data?.inviteToOrganizationByEmail.ok?.code;
|
||||
|
|
@ -41,7 +41,7 @@ test('can check a schema with target:registry:read access', async () => {
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -58,7 +58,7 @@ test('can check a schema with target:registry:read access', async () => {
|
|||
projectScopes: [],
|
||||
targetScopes: [TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
expect(writeTokenResult.body.errors).not.toBeDefined();
|
||||
const writeToken = writeTokenResult.body.data!.createToken.ok!.secret;
|
||||
|
|
@ -70,7 +70,7 @@ test('can check a schema with target:registry:read access', async () => {
|
|||
commit: 'abc123',
|
||||
sdl: `type Query { ping: String }`,
|
||||
},
|
||||
writeToken
|
||||
writeToken,
|
||||
);
|
||||
|
||||
// Schema publish should be successful
|
||||
|
|
@ -88,7 +88,7 @@ test('can check a schema with target:registry:read access', async () => {
|
|||
projectScopes: [],
|
||||
targetScopes: [],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
expect(noAccessTokenResult.body.errors).not.toBeDefined();
|
||||
|
||||
|
|
@ -103,7 +103,7 @@ test('can check a schema with target:registry:read access', async () => {
|
|||
projectScopes: [],
|
||||
targetScopes: [TargetAccessScope.RegistryRead],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
expect(readTokenResult.body.errors).not.toBeDefined();
|
||||
|
||||
|
|
@ -115,7 +115,7 @@ test('can check a schema with target:registry:read access', async () => {
|
|||
{
|
||||
sdl: `type Query { ping: String foo: String }`,
|
||||
},
|
||||
noAccessToken
|
||||
noAccessToken,
|
||||
);
|
||||
expect(checkResult.body.errors).toHaveLength(1);
|
||||
expect(checkResult.body.errors![0].message).toMatch('target:registry:read');
|
||||
|
|
@ -125,7 +125,7 @@ test('can check a schema with target:registry:read access', async () => {
|
|||
{
|
||||
sdl: `type Query { ping: String foo: String }`,
|
||||
},
|
||||
readToken
|
||||
readToken,
|
||||
);
|
||||
expect(checkResult.body.errors).not.toBeDefined();
|
||||
expect(checkResult.body.data!.schemaCheck.__typename).toBe('SchemaCheckSuccess');
|
||||
|
|
@ -137,7 +137,7 @@ test('should match indentation of previous description', async () => {
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
||||
|
|
@ -146,7 +146,7 @@ test('should match indentation of previous description', async () => {
|
|||
email: 'some@email.com',
|
||||
organization: org.cleanId,
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const inviteCode = invitationResult.body.data?.inviteToOrganizationByEmail.ok?.code;
|
||||
|
|
@ -162,7 +162,7 @@ test('should match indentation of previous description', async () => {
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -179,7 +179,7 @@ test('should match indentation of previous description', async () => {
|
|||
projectScopes: [],
|
||||
targetScopes: [TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
expect(writeTokenResult.body.errors).not.toBeDefined();
|
||||
const writeToken = writeTokenResult.body.data!.createToken.ok!.secret;
|
||||
|
|
@ -198,7 +198,7 @@ test('should match indentation of previous description', async () => {
|
|||
}
|
||||
`,
|
||||
},
|
||||
writeToken
|
||||
writeToken,
|
||||
);
|
||||
|
||||
// Schema publish should be successful
|
||||
|
|
@ -216,7 +216,7 @@ test('should match indentation of previous description', async () => {
|
|||
projectScopes: [],
|
||||
targetScopes: [TargetAccessScope.RegistryRead],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
expect(readTokenResult.body.errors).not.toBeDefined();
|
||||
|
||||
|
|
@ -236,7 +236,7 @@ test('should match indentation of previous description', async () => {
|
|||
}
|
||||
`,
|
||||
},
|
||||
readToken
|
||||
readToken,
|
||||
);
|
||||
expect(checkResult.body.errors).not.toBeDefined();
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ test('call an external service to compose and validate services', async () => {
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
||||
|
|
@ -25,7 +25,7 @@ test('call an external service to compose and validate services', async () => {
|
|||
type: ProjectType.Federation,
|
||||
name: 'bar',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -42,7 +42,7 @@ test('call an external service to compose and validate services', async () => {
|
|||
projectScopes: [ProjectAccessScope.Settings, ProjectAccessScope.Read],
|
||||
targetScopes: [TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
expect(writeTokenResult.body.errors).not.toBeDefined();
|
||||
const writeToken = writeTokenResult.body.data!.createToken.ok!.secret;
|
||||
|
|
@ -56,7 +56,7 @@ test('call an external service to compose and validate services', async () => {
|
|||
sdl: `type Query { me: User } type User @key(fields: "id") { id: ID! name: String }`,
|
||||
service: usersServiceName,
|
||||
},
|
||||
writeToken
|
||||
writeToken,
|
||||
);
|
||||
|
||||
// Schema publish should be successful
|
||||
|
|
@ -70,15 +70,16 @@ test('call an external service to compose and validate services', async () => {
|
|||
const externalCompositionResult = await enableExternalSchemaComposition(
|
||||
{
|
||||
endpoint: `http://${dockerAddress}/compose`,
|
||||
// eslint-disable-next-line no-process-env
|
||||
secret: process.env.EXTERNAL_COMPOSITION_SECRET!,
|
||||
project: project.cleanId,
|
||||
organization: org.cleanId,
|
||||
},
|
||||
writeToken
|
||||
writeToken,
|
||||
);
|
||||
expect(externalCompositionResult.body.errors).not.toBeDefined();
|
||||
expect(externalCompositionResult.body.data!.enableExternalSchemaComposition.ok?.endpoint).toBe(
|
||||
`http://${dockerAddress}/compose`
|
||||
`http://${dockerAddress}/compose`,
|
||||
);
|
||||
|
||||
const productsServiceName = Math.random().toString(16).substring(2);
|
||||
|
|
@ -90,7 +91,7 @@ test('call an external service to compose and validate services', async () => {
|
|||
sdl: `type Query { products: [Product] } type Product @key(fields: "id") { id: ID! name: String }`,
|
||||
service: productsServiceName,
|
||||
},
|
||||
writeToken
|
||||
writeToken,
|
||||
);
|
||||
|
||||
// Schema publish should be successful
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ test('cannot publish a schema without target:registry:write access', async () =>
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
// Join
|
||||
|
|
@ -37,7 +37,7 @@ test('cannot publish a schema without target:registry:write access', async () =>
|
|||
email: 'some@email.com',
|
||||
organization: org.cleanId,
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const inviteCode = invitationResult.body.data?.inviteToOrganizationByEmail.ok?.code;
|
||||
|
|
@ -51,7 +51,7 @@ test('cannot publish a schema without target:registry:write access', async () =>
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -67,7 +67,7 @@ test('cannot publish a schema without target:registry:write access', async () =>
|
|||
projectScopes: [],
|
||||
targetScopes: [TargetAccessScope.RegistryRead],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
expect(tokenResult.body.errors).not.toBeDefined();
|
||||
|
||||
|
|
@ -78,7 +78,7 @@ test('cannot publish a schema without target:registry:write access', async () =>
|
|||
commit: 'abc123',
|
||||
sdl: `type Query { ping: String }`,
|
||||
},
|
||||
token
|
||||
token,
|
||||
);
|
||||
|
||||
expect(result.body.errors).toHaveLength(1);
|
||||
|
|
@ -91,7 +91,7 @@ test('can publish a schema with target:registry:write access', async () => {
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
// Join
|
||||
|
|
@ -103,7 +103,7 @@ test('can publish a schema with target:registry:write access', async () => {
|
|||
email: 'some@email.com',
|
||||
organization: org.cleanId,
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const inviteCode = invitationResult.body.data?.inviteToOrganizationByEmail.ok?.code;
|
||||
|
|
@ -117,7 +117,7 @@ test('can publish a schema with target:registry:write access', async () => {
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -133,7 +133,7 @@ test('can publish a schema with target:registry:write access', async () => {
|
|||
projectScopes: [],
|
||||
targetScopes: [TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
expect(tokenResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -146,7 +146,7 @@ test('can publish a schema with target:registry:write access', async () => {
|
|||
commit: 'abc123',
|
||||
sdl: `type Query { ping: String }`,
|
||||
},
|
||||
token
|
||||
token,
|
||||
);
|
||||
|
||||
expect(result.body.errors).not.toBeDefined();
|
||||
|
|
@ -158,7 +158,7 @@ test('can publish a schema with target:registry:write access', async () => {
|
|||
commit: 'abc123',
|
||||
sdl: `type Query { ping: String pong: String }`,
|
||||
},
|
||||
token
|
||||
token,
|
||||
);
|
||||
|
||||
expect(result.body.errors).not.toBeDefined();
|
||||
|
|
@ -171,7 +171,7 @@ test('can publish a schema with target:registry:write access', async () => {
|
|||
target: target.cleanId,
|
||||
},
|
||||
3,
|
||||
token
|
||||
token,
|
||||
);
|
||||
|
||||
expect(versionsResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -184,7 +184,7 @@ test('base schema should not affect the output schema persisted in db', async ()
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
||||
|
|
@ -194,7 +194,7 @@ test('base schema should not affect the output schema persisted in db', async ()
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -211,7 +211,7 @@ test('base schema should not affect the output schema persisted in db', async ()
|
|||
projectScopes: [],
|
||||
targetScopes: [TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
expect(writeTokenResult.body.errors).not.toBeDefined();
|
||||
const writeToken = writeTokenResult.body.data!.createToken.ok!.secret;
|
||||
|
|
@ -223,7 +223,7 @@ test('base schema should not affect the output schema persisted in db', async ()
|
|||
commit: 'abc123',
|
||||
sdl: `type Query { ping: String }`,
|
||||
},
|
||||
writeToken
|
||||
writeToken,
|
||||
);
|
||||
|
||||
// Schema publish should be successful
|
||||
|
|
@ -239,7 +239,7 @@ test('base schema should not affect the output schema persisted in db', async ()
|
|||
project: project.cleanId,
|
||||
target: target.cleanId,
|
||||
},
|
||||
writeToken
|
||||
writeToken,
|
||||
);
|
||||
expect(updateBaseResult.body.errors).not.toBeDefined();
|
||||
|
||||
|
|
@ -250,7 +250,7 @@ test('base schema should not affect the output schema persisted in db', async ()
|
|||
author: 'Kamil',
|
||||
commit: 'abc234',
|
||||
},
|
||||
writeToken
|
||||
writeToken,
|
||||
);
|
||||
expect(publishResult.body.errors).not.toBeDefined();
|
||||
expect(publishResult.body.data!.schemaPublish.__typename).toBe('SchemaPublishSuccess');
|
||||
|
|
@ -262,7 +262,7 @@ test('base schema should not affect the output schema persisted in db', async ()
|
|||
target: target.cleanId,
|
||||
},
|
||||
5,
|
||||
writeToken
|
||||
writeToken,
|
||||
);
|
||||
|
||||
expect(versionsResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -273,10 +273,12 @@ test('base schema should not affect the output schema persisted in db', async ()
|
|||
expect(latestResult.body.data!.latestVersion.schemas.total).toBe(1);
|
||||
expect(latestResult.body.data!.latestVersion.schemas.nodes[0].commit).toBe('abc234');
|
||||
expect(latestResult.body.data!.latestVersion.schemas.nodes[0].source).toMatch(
|
||||
'type Query { ping: String @auth pong: String }'
|
||||
'type Query { ping: String @auth pong: String }',
|
||||
);
|
||||
expect(latestResult.body.data!.latestVersion.schemas.nodes[0].source).not.toMatch('directive');
|
||||
expect(latestResult.body.data!.latestVersion.baseSchema).toMatch('directive @auth on OBJECT | FIELD_DEFINITION');
|
||||
expect(latestResult.body.data!.latestVersion.baseSchema).toMatch(
|
||||
'directive @auth on OBJECT | FIELD_DEFINITION',
|
||||
);
|
||||
});
|
||||
|
||||
test('directives should not be removed (federation)', async () => {
|
||||
|
|
@ -285,7 +287,7 @@ test('directives should not be removed (federation)', async () => {
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
||||
|
|
@ -295,7 +297,7 @@ test('directives should not be removed (federation)', async () => {
|
|||
type: ProjectType.Federation,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -312,7 +314,7 @@ test('directives should not be removed (federation)', async () => {
|
|||
projectScopes: [],
|
||||
targetScopes: [TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
expect(writeTokenResult.body.errors).not.toBeDefined();
|
||||
const writeToken = writeTokenResult.body.data!.createToken.ok!.secret;
|
||||
|
|
@ -326,7 +328,7 @@ test('directives should not be removed (federation)', async () => {
|
|||
service: 'users',
|
||||
sdl: `type Query { me: User } type User @key(fields: "id") { id: ID! name: String }`,
|
||||
},
|
||||
writeToken
|
||||
writeToken,
|
||||
);
|
||||
|
||||
// Schema publish should be successful
|
||||
|
|
@ -340,7 +342,7 @@ test('directives should not be removed (federation)', async () => {
|
|||
target: target.cleanId,
|
||||
},
|
||||
5,
|
||||
writeToken
|
||||
writeToken,
|
||||
);
|
||||
|
||||
expect(versionsResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -351,7 +353,7 @@ test('directives should not be removed (federation)', async () => {
|
|||
expect(latestResult.body.data!.latestVersion.schemas.total).toBe(1);
|
||||
expect(latestResult.body.data!.latestVersion.schemas.nodes[0].commit).toBe('abc123');
|
||||
expect(latestResult.body.data!.latestVersion.schemas.nodes[0].source).toMatch(
|
||||
`type Query { me: User } type User @key(fields: "id") { id: ID! name: String }`
|
||||
`type Query { me: User } type User @key(fields: "id") { id: ID! name: String }`,
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -361,7 +363,7 @@ test('should allow to update the URL of a Federated service without changing the
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
||||
|
|
@ -371,7 +373,7 @@ test('should allow to update the URL of a Federated service without changing the
|
|||
type: ProjectType.Federation,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -388,7 +390,7 @@ test('should allow to update the URL of a Federated service without changing the
|
|||
projectScopes: [],
|
||||
targetScopes: [TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
expect(writeTokenResult.body.errors).not.toBeDefined();
|
||||
const writeToken = writeTokenResult.body.data!.createToken.ok!.secret;
|
||||
|
|
@ -414,7 +416,7 @@ test('should allow to update the URL of a Federated service without changing the
|
|||
target: target.cleanId,
|
||||
},
|
||||
5,
|
||||
writeToken
|
||||
writeToken,
|
||||
);
|
||||
|
||||
expect(versionsResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -427,22 +429,24 @@ test('should allow to update the URL of a Federated service without changing the
|
|||
url: `http://localhost:3000/test/graphql`,
|
||||
commit: 'abc1234',
|
||||
},
|
||||
writeToken
|
||||
writeToken,
|
||||
);
|
||||
|
||||
expect(updateResult.body.errors).not.toBeDefined();
|
||||
expect(updateResult.body.data!.schemaPublish.__typename).toBe('SchemaPublishSuccess');
|
||||
expect((updateResult.body.data!.schemaPublish as any).message).toBe(
|
||||
'Updated: New service url: http://localhost:3000/test/graphql (previously: https://api.com/users)'
|
||||
'Updated: New service url: http://localhost:3000/test/graphql (previously: https://api.com/users)',
|
||||
);
|
||||
|
||||
const latestResult = await fetchLatestSchema(writeToken);
|
||||
expect(latestResult.body.errors).not.toBeDefined();
|
||||
expect(latestResult.body.data!.latestVersion.schemas.total).toBe(1);
|
||||
expect(latestResult.body.data!.latestVersion.schemas.nodes[0].commit).toBe('abc1234');
|
||||
expect(latestResult.body.data!.latestVersion.schemas.nodes[0].url).toBe('http://localhost:3000/test/graphql');
|
||||
expect(latestResult.body.data!.latestVersion.schemas.nodes[0].url).toBe(
|
||||
'http://localhost:3000/test/graphql',
|
||||
);
|
||||
expect(latestResult.body.data!.latestVersion.schemas.nodes[0].source).toMatch(
|
||||
`type Query { me: User } type User @key(fields: "id") { id: ID! name: String }`
|
||||
`type Query { me: User } type User @key(fields: "id") { id: ID! name: String }`,
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -452,7 +456,7 @@ test('should allow to update the URL of a Federated service while also changing
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
||||
|
|
@ -462,7 +466,7 @@ test('should allow to update the URL of a Federated service while also changing
|
|||
type: ProjectType.Federation,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -479,7 +483,7 @@ test('should allow to update the URL of a Federated service while also changing
|
|||
projectScopes: [],
|
||||
targetScopes: [TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
expect(writeTokenResult.body.errors).not.toBeDefined();
|
||||
const writeToken = writeTokenResult.body.data!.createToken.ok!.secret;
|
||||
|
|
@ -505,7 +509,7 @@ test('should allow to update the URL of a Federated service while also changing
|
|||
target: target.cleanId,
|
||||
},
|
||||
5,
|
||||
writeToken
|
||||
writeToken,
|
||||
);
|
||||
|
||||
expect(versionsResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -516,7 +520,7 @@ test('should allow to update the URL of a Federated service while also changing
|
|||
expect(latestResult.body.data!.latestVersion.schemas.total).toBe(1);
|
||||
expect(latestResult.body.data!.latestVersion.schemas.nodes[0].commit).toBe('abc123');
|
||||
expect(latestResult.body.data!.latestVersion.schemas.nodes[0].source).toMatch(
|
||||
`type Query { me: User } type User @key(fields: "id") { id: ID! name: String }`
|
||||
`type Query { me: User } type User @key(fields: "id") { id: ID! name: String }`,
|
||||
);
|
||||
|
||||
// try to update the schema again, with force and url set
|
||||
|
|
@ -528,7 +532,7 @@ test('should allow to update the URL of a Federated service while also changing
|
|||
// here, we also add something minor to the schema, just to trigger the publish flow and not just the URL update flow
|
||||
sdl: `type Query { me: User } type User @key(fields: "id") { id: ID! name: String age: Int }`,
|
||||
},
|
||||
writeToken
|
||||
writeToken,
|
||||
);
|
||||
|
||||
expect(updateResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -541,7 +545,7 @@ test('directives should not be removed (stitching)', async () => {
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
||||
|
|
@ -551,7 +555,7 @@ test('directives should not be removed (stitching)', async () => {
|
|||
type: ProjectType.Stitching,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -568,7 +572,7 @@ test('directives should not be removed (stitching)', async () => {
|
|||
projectScopes: [],
|
||||
targetScopes: [TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
expect(writeTokenResult.body.errors).not.toBeDefined();
|
||||
const writeToken = writeTokenResult.body.data!.createToken.ok!.secret;
|
||||
|
|
@ -582,7 +586,7 @@ test('directives should not be removed (stitching)', async () => {
|
|||
service: 'test',
|
||||
url: 'https://api.com/users',
|
||||
},
|
||||
writeToken
|
||||
writeToken,
|
||||
);
|
||||
|
||||
// Schema publish should be successful
|
||||
|
|
@ -596,7 +600,7 @@ test('directives should not be removed (stitching)', async () => {
|
|||
target: target.cleanId,
|
||||
},
|
||||
5,
|
||||
writeToken
|
||||
writeToken,
|
||||
);
|
||||
|
||||
expect(versionsResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -607,7 +611,7 @@ test('directives should not be removed (stitching)', async () => {
|
|||
expect(latestResult.body.data!.latestVersion.schemas.total).toBe(1);
|
||||
expect(latestResult.body.data!.latestVersion.schemas.nodes[0].commit).toBe('abc123');
|
||||
expect(latestResult.body.data!.latestVersion.schemas.nodes[0].source).toMatch(
|
||||
`type Query { me: User } type User @key(selectionSet: "{ id }") { id: ID! name: String }`
|
||||
`type Query { me: User } type User @key(selectionSet: "{ id }") { id: ID! name: String }`,
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -617,7 +621,7 @@ test('directives should not be removed (single)', async () => {
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
||||
|
|
@ -627,7 +631,7 @@ test('directives should not be removed (single)', async () => {
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -644,7 +648,7 @@ test('directives should not be removed (single)', async () => {
|
|||
projectScopes: [],
|
||||
targetScopes: [TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
expect(writeTokenResult.body.errors).not.toBeDefined();
|
||||
const writeToken = writeTokenResult.body.data!.createToken.ok!.secret;
|
||||
|
|
@ -658,7 +662,7 @@ test('directives should not be removed (single)', async () => {
|
|||
service: 'test',
|
||||
url: 'https://api.com/users',
|
||||
},
|
||||
writeToken
|
||||
writeToken,
|
||||
);
|
||||
|
||||
// Schema publish should be successful
|
||||
|
|
@ -672,7 +676,7 @@ test('directives should not be removed (single)', async () => {
|
|||
target: target.cleanId,
|
||||
},
|
||||
5,
|
||||
writeToken
|
||||
writeToken,
|
||||
);
|
||||
|
||||
expect(versionsResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -683,7 +687,7 @@ test('directives should not be removed (single)', async () => {
|
|||
expect(latestResult.body.data!.latestVersion.schemas.total).toBe(1);
|
||||
expect(latestResult.body.data!.latestVersion.schemas.nodes[0].commit).toBe('abc123');
|
||||
expect(latestResult.body.data!.latestVersion.schemas.nodes[0].source).toMatch(
|
||||
`directive @auth on FIELD_DEFINITION type Query { me: User @auth } type User { id: ID! name: String }`
|
||||
`directive @auth on FIELD_DEFINITION type Query { me: User @auth } type User { id: ID! name: String }`,
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -693,7 +697,7 @@ test('share publication of schema using redis', async () => {
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
||||
|
|
@ -703,7 +707,7 @@ test('share publication of schema using redis', async () => {
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -720,7 +724,7 @@ test('share publication of schema using redis', async () => {
|
|||
projectScopes: [],
|
||||
targetScopes: [TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
expect(writeTokenResult.body.errors).not.toBeDefined();
|
||||
const writeToken = writeTokenResult.body.data!.createToken.ok!.secret;
|
||||
|
|
@ -732,7 +736,7 @@ test('share publication of schema using redis', async () => {
|
|||
commit: 'abc123',
|
||||
sdl: `type Query { ping: String }`,
|
||||
},
|
||||
writeToken
|
||||
writeToken,
|
||||
);
|
||||
|
||||
// Schema publish should be successful
|
||||
|
|
@ -746,7 +750,7 @@ test('share publication of schema using redis', async () => {
|
|||
author: 'Kamil',
|
||||
commit: 'abc234',
|
||||
},
|
||||
writeToken
|
||||
writeToken,
|
||||
),
|
||||
publishSchema(
|
||||
{
|
||||
|
|
@ -754,7 +758,7 @@ test('share publication of schema using redis', async () => {
|
|||
author: 'Kamil',
|
||||
commit: 'abc234',
|
||||
},
|
||||
writeToken
|
||||
writeToken,
|
||||
),
|
||||
]);
|
||||
expect(publishResult1.body.errors).not.toBeDefined();
|
||||
|
|
@ -769,7 +773,7 @@ test("Two targets with the same commit id shouldn't return an error", async () =
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
const projectResult = await createProject(
|
||||
|
|
@ -778,7 +782,7 @@ test("Two targets with the same commit id shouldn't return an error", async () =
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
const target = projectResult.body.data!.createProject.ok!.createdTargets[0];
|
||||
|
|
@ -792,7 +796,7 @@ test("Two targets with the same commit id shouldn't return an error", async () =
|
|||
projectScopes: [],
|
||||
targetScopes: [TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
expect(writeTokenResult.body.errors).not.toBeDefined();
|
||||
const writeToken = writeTokenResult.body.data!.createToken.ok!.secret;
|
||||
|
|
@ -802,7 +806,7 @@ test("Two targets with the same commit id shouldn't return an error", async () =
|
|||
commit: 'abc123',
|
||||
sdl: `type Query { ping: String }`,
|
||||
},
|
||||
writeToken
|
||||
writeToken,
|
||||
);
|
||||
const createTargetResult = await createTarget(
|
||||
{
|
||||
|
|
@ -810,7 +814,7 @@ test("Two targets with the same commit id shouldn't return an error", async () =
|
|||
project: project.cleanId,
|
||||
name: 'target2',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
const target2 = createTargetResult.body!.data!.createTarget.ok!.createdTarget;
|
||||
const writeTokenResult2 = await createToken(
|
||||
|
|
@ -823,7 +827,7 @@ test("Two targets with the same commit id shouldn't return an error", async () =
|
|||
projectScopes: [],
|
||||
targetScopes: [TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
const writeToken2 = writeTokenResult2.body.data!.createToken.ok!.secret;
|
||||
const publishResult2 = await publishSchema(
|
||||
|
|
@ -832,7 +836,7 @@ test("Two targets with the same commit id shouldn't return an error", async () =
|
|||
commit: 'abc123',
|
||||
sdl: `type Query { ping: String }`,
|
||||
},
|
||||
writeToken2
|
||||
writeToken2,
|
||||
);
|
||||
// Schema publish should be successful
|
||||
expect(publishResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -847,7 +851,7 @@ test('marking versions as valid', async () => {
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
// Join
|
||||
|
|
@ -859,7 +863,7 @@ test('marking versions as valid', async () => {
|
|||
email: 'some@email.com',
|
||||
organization: org.cleanId,
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const inviteCode = invitationResult.body.data?.inviteToOrganizationByEmail.ok?.code;
|
||||
|
|
@ -873,7 +877,7 @@ test('marking versions as valid', async () => {
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -889,7 +893,7 @@ test('marking versions as valid', async () => {
|
|||
projectScopes: [],
|
||||
targetScopes: [TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
expect(tokenResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -903,7 +907,7 @@ test('marking versions as valid', async () => {
|
|||
commit: 'c0',
|
||||
sdl: `type Query { ping: String }`,
|
||||
},
|
||||
token
|
||||
token,
|
||||
);
|
||||
|
||||
expect(result.body.errors).not.toBeDefined();
|
||||
|
|
@ -918,7 +922,7 @@ test('marking versions as valid', async () => {
|
|||
force: true,
|
||||
metadata: JSON.stringify({ c1: true }),
|
||||
},
|
||||
token
|
||||
token,
|
||||
);
|
||||
|
||||
expect(result.body.errors).not.toBeDefined();
|
||||
|
|
@ -932,7 +936,7 @@ test('marking versions as valid', async () => {
|
|||
force: true,
|
||||
metadata: JSON.stringify({ c2: true }),
|
||||
},
|
||||
token
|
||||
token,
|
||||
);
|
||||
|
||||
expect(result.body.errors).not.toBeDefined();
|
||||
|
|
@ -944,7 +948,7 @@ test('marking versions as valid', async () => {
|
|||
target: target.cleanId,
|
||||
},
|
||||
3,
|
||||
token
|
||||
token,
|
||||
);
|
||||
|
||||
expect(versionsResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -954,7 +958,9 @@ test('marking versions as valid', async () => {
|
|||
let latestValidSchemaResult = await fetchLatestValidSchema(token);
|
||||
expect(latestValidSchemaResult.body.errors).not.toBeDefined();
|
||||
expect(latestValidSchemaResult.body.data!.latestValidVersion.schemas.total).toEqual(1);
|
||||
expect(latestValidSchemaResult.body.data!.latestValidVersion.schemas.nodes[0].commit).toEqual('c0');
|
||||
expect(latestValidSchemaResult.body.data!.latestValidVersion.schemas.nodes[0].commit).toEqual(
|
||||
'c0',
|
||||
);
|
||||
|
||||
const versionId = (commit: string) =>
|
||||
versionsResult.body.data!.schemaVersions.nodes.find(node => node.commit.commit === commit)!.id;
|
||||
|
|
@ -968,11 +974,13 @@ test('marking versions as valid', async () => {
|
|||
valid: true,
|
||||
version: versionId('c2'),
|
||||
},
|
||||
token
|
||||
token,
|
||||
);
|
||||
|
||||
expect(versionStatusUpdateResult.body.errors).not.toBeDefined();
|
||||
expect(versionStatusUpdateResult.body.data!.updateSchemaVersionStatus.id).toEqual(versionId('c2'));
|
||||
expect(versionStatusUpdateResult.body.data!.updateSchemaVersionStatus.id).toEqual(
|
||||
versionId('c2'),
|
||||
);
|
||||
|
||||
latestValidSchemaResult = await fetchLatestValidSchema(token);
|
||||
expect(latestValidSchemaResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -987,7 +995,7 @@ test('marking versions as valid', async () => {
|
|||
valid: true,
|
||||
version: versionId('c1'),
|
||||
},
|
||||
token
|
||||
token,
|
||||
);
|
||||
expect(versionStatusUpdateResult.body.errors).not.toBeDefined();
|
||||
|
||||
|
|
@ -1002,7 +1010,7 @@ test('marking only the most recent version as valid result in an update of CDN',
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
// Join
|
||||
|
|
@ -1014,7 +1022,7 @@ test('marking only the most recent version as valid result in an update of CDN',
|
|||
email: 'some@email.com',
|
||||
organization: org.cleanId,
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const inviteCode = invitationResult.body.data?.inviteToOrganizationByEmail.ok?.code;
|
||||
|
|
@ -1028,7 +1036,7 @@ test('marking only the most recent version as valid result in an update of CDN',
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -1044,7 +1052,7 @@ test('marking only the most recent version as valid result in an update of CDN',
|
|||
projectScopes: [],
|
||||
targetScopes: [TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
expect(tokenResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -1059,7 +1067,7 @@ test('marking only the most recent version as valid result in an update of CDN',
|
|||
sdl: `type Query { ping: String }`,
|
||||
metadata: JSON.stringify({ c0: 1 }),
|
||||
},
|
||||
token
|
||||
token,
|
||||
);
|
||||
|
||||
expect(result.body.errors).not.toBeDefined();
|
||||
|
|
@ -1074,7 +1082,7 @@ test('marking only the most recent version as valid result in an update of CDN',
|
|||
force: true,
|
||||
metadata: JSON.stringify({ c1: 1 }),
|
||||
},
|
||||
token
|
||||
token,
|
||||
);
|
||||
|
||||
expect(result.body.errors).not.toBeDefined();
|
||||
|
|
@ -1088,7 +1096,7 @@ test('marking only the most recent version as valid result in an update of CDN',
|
|||
force: true,
|
||||
metadata: JSON.stringify({ c2: 1 }),
|
||||
},
|
||||
token
|
||||
token,
|
||||
);
|
||||
|
||||
expect(result.body.errors).not.toBeDefined();
|
||||
|
|
@ -1121,7 +1129,7 @@ test('marking only the most recent version as valid result in an update of CDN',
|
|||
valid: true,
|
||||
version: versionId('c2'),
|
||||
},
|
||||
token
|
||||
token,
|
||||
);
|
||||
|
||||
cdnResult = await fetchSchemaFromCDN(targetSelector, token);
|
||||
|
|
@ -1141,7 +1149,7 @@ test('marking only the most recent version as valid result in an update of CDN',
|
|||
valid: true,
|
||||
version: versionId('c1'),
|
||||
},
|
||||
token
|
||||
token,
|
||||
);
|
||||
// console.log(JSON.stringify(updateSchemaVersionStatusResult));
|
||||
|
||||
|
|
@ -1159,7 +1167,7 @@ test('CDN data can not be fetched with an invalid access token', async () => {
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
// Join
|
||||
|
|
@ -1171,7 +1179,7 @@ test('CDN data can not be fetched with an invalid access token', async () => {
|
|||
email: 'some@email.com',
|
||||
organization: org.cleanId,
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const inviteCode = invitationResult.body.data?.inviteToOrganizationByEmail.ok?.code;
|
||||
|
|
@ -1185,7 +1193,7 @@ test('CDN data can not be fetched with an invalid access token', async () => {
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -1201,7 +1209,7 @@ test('CDN data can not be fetched with an invalid access token', async () => {
|
|||
projectScopes: [],
|
||||
targetScopes: [TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
expect(tokenResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -1216,7 +1224,7 @@ test('CDN data can not be fetched with an invalid access token', async () => {
|
|||
sdl: `type Query { ping: String }`,
|
||||
metadata: JSON.stringify({ c0: 1 }),
|
||||
},
|
||||
token
|
||||
token,
|
||||
);
|
||||
|
||||
expect(result.body.errors).not.toBeDefined();
|
||||
|
|
@ -1253,7 +1261,7 @@ test('CDN data can be fetched with an valid access token', async () => {
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
// Join
|
||||
|
|
@ -1265,7 +1273,7 @@ test('CDN data can be fetched with an valid access token', async () => {
|
|||
email: 'some@email.com',
|
||||
organization: org.cleanId,
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const inviteCode = invitationResult.body.data?.inviteToOrganizationByEmail.ok?.code;
|
||||
|
|
@ -1279,7 +1287,7 @@ test('CDN data can be fetched with an valid access token', async () => {
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -1295,7 +1303,7 @@ test('CDN data can be fetched with an valid access token', async () => {
|
|||
projectScopes: [],
|
||||
targetScopes: [TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
expect(tokenResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -1310,7 +1318,7 @@ test('CDN data can be fetched with an valid access token', async () => {
|
|||
sdl: `type Query { ping: String }`,
|
||||
metadata: JSON.stringify({ c0: 1 }),
|
||||
},
|
||||
token
|
||||
token,
|
||||
);
|
||||
|
||||
expect(result.body.errors).not.toBeDefined();
|
||||
|
|
@ -1347,7 +1355,7 @@ test('linkToWebsite should be available when publishing initial schema', async (
|
|||
{
|
||||
name: 'bar',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
// Join
|
||||
|
|
@ -1359,7 +1367,7 @@ test('linkToWebsite should be available when publishing initial schema', async (
|
|||
email: 'some@email.com',
|
||||
organization: org.cleanId,
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const inviteCode = invitationResult.body.data?.inviteToOrganizationByEmail.ok?.code;
|
||||
|
|
@ -1373,7 +1381,7 @@ test('linkToWebsite should be available when publishing initial schema', async (
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -1390,7 +1398,7 @@ test('linkToWebsite should be available when publishing initial schema', async (
|
|||
projectScopes: [],
|
||||
targetScopes: [TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
expect(tokenResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -1403,7 +1411,7 @@ test('linkToWebsite should be available when publishing initial schema', async (
|
|||
commit: 'abc123',
|
||||
sdl: `type Query { ping: String }`,
|
||||
},
|
||||
token
|
||||
token,
|
||||
);
|
||||
|
||||
expect(result.body.errors).not.toBeDefined();
|
||||
|
|
@ -1423,7 +1431,7 @@ test('linkToWebsite should be available when publishing non-initial schema', asy
|
|||
{
|
||||
name: 'bar',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
// Join
|
||||
|
|
@ -1435,7 +1443,7 @@ test('linkToWebsite should be available when publishing non-initial schema', asy
|
|||
email: 'some@email.com',
|
||||
organization: org.cleanId,
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const inviteCode = invitationResult.body.data?.inviteToOrganizationByEmail.ok?.code;
|
||||
|
|
@ -1449,7 +1457,7 @@ test('linkToWebsite should be available when publishing non-initial schema', asy
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -1466,7 +1474,7 @@ test('linkToWebsite should be available when publishing non-initial schema', asy
|
|||
projectScopes: [],
|
||||
targetScopes: [TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
expect(tokenResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -1479,7 +1487,7 @@ test('linkToWebsite should be available when publishing non-initial schema', asy
|
|||
commit: 'abc123',
|
||||
sdl: `type Query { ping: String }`,
|
||||
},
|
||||
token
|
||||
token,
|
||||
);
|
||||
|
||||
expect(result.body.errors).not.toBeDefined();
|
||||
|
|
@ -1491,7 +1499,7 @@ test('linkToWebsite should be available when publishing non-initial schema', asy
|
|||
commit: 'abc123',
|
||||
sdl: `type Query { ping: String pong: String }`,
|
||||
},
|
||||
token
|
||||
token,
|
||||
);
|
||||
|
||||
expect(result.body.errors).not.toBeDefined();
|
||||
|
|
@ -1513,7 +1521,7 @@ test('cannot do API request with invalid access token', async () => {
|
|||
sdl: 'type Query { smokeBangBang: String }',
|
||||
author: 'Kamil',
|
||||
},
|
||||
'foobars'
|
||||
'foobars',
|
||||
);
|
||||
expect(orgResult).toEqual({
|
||||
body: {
|
||||
|
|
@ -1541,7 +1549,7 @@ test('publish new schema when a field is moved from one service to another (stit
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
access_token
|
||||
access_token,
|
||||
);
|
||||
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
|
@ -1552,7 +1560,7 @@ test('publish new schema when a field is moved from one service to another (stit
|
|||
type: ProjectType.Stitching,
|
||||
name: 'foo',
|
||||
},
|
||||
access_token
|
||||
access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -1568,7 +1576,7 @@ test('publish new schema when a field is moved from one service to another (stit
|
|||
projectScopes: [],
|
||||
targetScopes: [TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
},
|
||||
access_token
|
||||
access_token,
|
||||
);
|
||||
|
||||
expect(tokenResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -1587,7 +1595,7 @@ test('publish new schema when a field is moved from one service to another (stit
|
|||
`,
|
||||
service: 'cats',
|
||||
},
|
||||
token
|
||||
token,
|
||||
);
|
||||
|
||||
expect(result.body.errors).not.toBeDefined();
|
||||
|
|
@ -1606,7 +1614,7 @@ test('publish new schema when a field is moved from one service to another (stit
|
|||
`,
|
||||
service: 'dogs',
|
||||
},
|
||||
token
|
||||
token,
|
||||
);
|
||||
|
||||
expect(result.body.errors).not.toBeDefined();
|
||||
|
|
@ -1625,7 +1633,7 @@ test('publish new schema when a field is moved from one service to another (stit
|
|||
`,
|
||||
service: 'cats',
|
||||
},
|
||||
token
|
||||
token,
|
||||
);
|
||||
|
||||
// We expect to have a new version, even tough the schema (merged) is the same
|
||||
|
|
@ -1640,7 +1648,7 @@ test('publish new schema when a field is moved from one service to another (stit
|
|||
target: target.cleanId,
|
||||
},
|
||||
3,
|
||||
token
|
||||
token,
|
||||
);
|
||||
|
||||
expect(versionsResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -1653,7 +1661,7 @@ test('(experimental_acceptBreakingChanges) accept breaking changes if schema is
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
||||
|
|
@ -1663,7 +1671,7 @@ test('(experimental_acceptBreakingChanges) accept breaking changes if schema is
|
|||
type: ProjectType.Federation,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -1680,7 +1688,7 @@ test('(experimental_acceptBreakingChanges) accept breaking changes if schema is
|
|||
projectScopes: [],
|
||||
targetScopes: [TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
expect(writeTokenResult.body.errors).not.toBeDefined();
|
||||
const writeToken = writeTokenResult.body.data!.createToken.ok!.secret;
|
||||
|
|
@ -1710,11 +1718,15 @@ test('(experimental_acceptBreakingChanges) accept breaking changes if schema is
|
|||
// We also removed the `name` field (breaking)
|
||||
sdl: `type Query { me: User } type User @key(fields: "id") { id: ID! }`,
|
||||
},
|
||||
writeToken
|
||||
writeToken,
|
||||
);
|
||||
|
||||
const latestValid = await fetchLatestValidSchema(writeToken);
|
||||
|
||||
expect(composableButBreakingResult.body.data!.schemaPublish.__typename).toBe('SchemaPublishSuccess');
|
||||
expect(latestValid.body.data?.latestValidVersion.schemas.nodes[0].commit).toBe('composable-but-breaking');
|
||||
expect(composableButBreakingResult.body.data!.schemaPublish.__typename).toBe(
|
||||
'SchemaPublishSuccess',
|
||||
);
|
||||
expect(latestValid.body.data?.latestValidVersion.schemas.nodes[0].commit).toBe(
|
||||
'composable-but-breaking',
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ test('marking only the most recent version as valid result in an update of CDN',
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
|
@ -26,7 +26,7 @@ test('marking only the most recent version as valid result in an update of CDN',
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -42,7 +42,7 @@ test('marking only the most recent version as valid result in an update of CDN',
|
|||
projectScopes: [],
|
||||
targetScopes: [TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
expect(tokenResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -56,7 +56,7 @@ test('marking only the most recent version as valid result in an update of CDN',
|
|||
commit: 'c0',
|
||||
sdl: `type Query { ping: String }`,
|
||||
},
|
||||
token
|
||||
token,
|
||||
);
|
||||
|
||||
expect(publishResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -79,7 +79,7 @@ test('marking only the most recent version as valid result in an update of CDN',
|
|||
project: project.cleanId,
|
||||
target: target.cleanId,
|
||||
},
|
||||
token
|
||||
token,
|
||||
);
|
||||
|
||||
expect(syncResult.body.errors).not.toBeDefined();
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ test('renaming a target should result changing its cleanId', async () => {
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
access_token
|
||||
access_token,
|
||||
);
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
||||
|
|
@ -18,11 +18,13 @@ test('renaming a target should result changing its cleanId', async () => {
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
access_token
|
||||
access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
const target = projectResult.body.data?.createProject.ok?.createdTargets.find(t => t.name === 'production');
|
||||
const target = projectResult.body.data?.createProject.ok?.createdTargets.find(
|
||||
t => t.name === 'production',
|
||||
);
|
||||
|
||||
expect(target).toBeDefined();
|
||||
|
||||
|
|
@ -33,7 +35,7 @@ test('renaming a target should result changing its cleanId', async () => {
|
|||
target: target!.cleanId,
|
||||
name: 'bar',
|
||||
},
|
||||
access_token
|
||||
access_token,
|
||||
);
|
||||
|
||||
expect(renamedTargetResult.body.errors).not.toBeDefined();
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ test('setting no scopes equals to readonly for organization, project, target', a
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
|
@ -27,7 +27,7 @@ test('setting no scopes equals to readonly for organization, project, target', a
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -44,7 +44,7 @@ test('setting no scopes equals to readonly for organization, project, target', a
|
|||
projectScopes: [],
|
||||
targetScopes: [],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
expect(tokenResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -88,7 +88,7 @@ test('cannot set a scope on a token if user has no access to that scope', async
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
// Join
|
||||
|
|
@ -100,7 +100,7 @@ test('cannot set a scope on a token if user has no access to that scope', async
|
|||
email: 'some@email.com',
|
||||
organization: org.cleanId,
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const inviteCode = invitationResult.body.data?.inviteToOrganizationByEmail.ok?.code;
|
||||
|
|
@ -114,7 +114,7 @@ test('cannot set a scope on a token if user has no access to that scope', async
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
if (joinResult.body.data!.joinOrganization.__typename !== 'OrganizationPayload') {
|
||||
|
|
@ -139,7 +139,7 @@ test('cannot set a scope on a token if user has no access to that scope', async
|
|||
],
|
||||
user: member.id,
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
// member should not have access to target:registry:write
|
||||
|
|
@ -153,7 +153,7 @@ test('cannot set a scope on a token if user has no access to that scope', async
|
|||
projectScopes: [],
|
||||
targetScopes: [TargetAccessScope.RegistryWrite],
|
||||
},
|
||||
member_access_token
|
||||
member_access_token,
|
||||
);
|
||||
|
||||
expect(tokenResult.body.errors).toHaveLength(1);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,9 @@
|
|||
import { TargetAccessScope, ProjectType, ProjectAccessScope, OrganizationAccessScope } from '@app/gql/graphql';
|
||||
import {
|
||||
TargetAccessScope,
|
||||
ProjectType,
|
||||
ProjectAccessScope,
|
||||
OrganizationAccessScope,
|
||||
} from '@app/gql/graphql';
|
||||
import formatISO from 'date-fns/formatISO';
|
||||
import subHours from 'date-fns/subHours';
|
||||
import {
|
||||
|
|
@ -16,7 +21,7 @@ import {
|
|||
import { authenticate } from '../../../testkit/auth';
|
||||
import { collect, CollectedOperation } from '../../../testkit/usage';
|
||||
import { clickHouseQuery } from '../../../testkit/clickhouse';
|
||||
// eslint-disable-next-line hive/enforce-deps-in-dev, import/no-extraneous-dependencies
|
||||
// eslint-disable-next-line hive/enforce-deps-in-dev
|
||||
import { normalizeOperation } from '@graphql-hive/core';
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import { parse, print } from 'graphql';
|
||||
|
|
@ -49,7 +54,7 @@ test('collect operation', async () => {
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
|
@ -60,7 +65,7 @@ test('collect operation', async () => {
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -76,7 +81,7 @@ test('collect operation', async () => {
|
|||
projectScopes: [ProjectAccessScope.Read],
|
||||
targetScopes: [TargetAccessScope.Read, TargetAccessScope.Settings],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const tokenResult = await createToken(
|
||||
|
|
@ -87,9 +92,13 @@ test('collect operation', async () => {
|
|||
target: target.cleanId,
|
||||
organizationScopes: [OrganizationAccessScope.Read],
|
||||
projectScopes: [ProjectAccessScope.Read],
|
||||
targetScopes: [TargetAccessScope.Read, TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
targetScopes: [
|
||||
TargetAccessScope.Read,
|
||||
TargetAccessScope.RegistryRead,
|
||||
TargetAccessScope.RegistryWrite,
|
||||
],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
expect(settingsTokenResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -104,7 +113,7 @@ test('collect operation', async () => {
|
|||
commit: 'abc123',
|
||||
sdl: `type Query { ping: String me: String }`,
|
||||
},
|
||||
token
|
||||
token,
|
||||
);
|
||||
|
||||
expect(schemaPublishResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -119,7 +128,7 @@ test('collect operation', async () => {
|
|||
},
|
||||
{
|
||||
token: tokenForSettings,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
expect(targetValidationResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -132,7 +141,7 @@ test('collect operation', async () => {
|
|||
{
|
||||
sdl: `type Query { me: String }`,
|
||||
},
|
||||
token
|
||||
token,
|
||||
);
|
||||
expect(unusedCheckResult.body.errors).not.toBeDefined();
|
||||
expect(unusedCheckResult.body.data!.schemaCheck.__typename).toEqual('SchemaCheckSuccess');
|
||||
|
|
@ -162,11 +171,13 @@ test('collect operation', async () => {
|
|||
{
|
||||
sdl: `type Query { me: String }`,
|
||||
},
|
||||
token
|
||||
token,
|
||||
);
|
||||
|
||||
if (usedCheckResult.body.data!.schemaCheck.__typename !== 'SchemaCheckError') {
|
||||
throw new Error(`Expected SchemaCheckError, got ${usedCheckResult.body.data!.schemaCheck.__typename}`);
|
||||
throw new Error(
|
||||
`Expected SchemaCheckError, got ${usedCheckResult.body.data!.schemaCheck.__typename}`,
|
||||
);
|
||||
}
|
||||
|
||||
expect(usedCheckResult.body.data!.schemaCheck.valid).toEqual(false);
|
||||
|
|
@ -183,7 +194,7 @@ test('collect operation', async () => {
|
|||
to,
|
||||
},
|
||||
},
|
||||
token
|
||||
token,
|
||||
);
|
||||
|
||||
expect(operationStatsResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -212,7 +223,7 @@ test('normalize and collect operation without breaking its syntax', async () =>
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
|
@ -223,7 +234,7 @@ test('normalize and collect operation without breaking its syntax', async () =>
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -239,7 +250,7 @@ test('normalize and collect operation without breaking its syntax', async () =>
|
|||
projectScopes: [ProjectAccessScope.Read],
|
||||
targetScopes: [TargetAccessScope.Read, TargetAccessScope.Settings],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const tokenResult = await createToken(
|
||||
|
|
@ -250,9 +261,13 @@ test('normalize and collect operation without breaking its syntax', async () =>
|
|||
target: target.cleanId,
|
||||
organizationScopes: [OrganizationAccessScope.Read],
|
||||
projectScopes: [ProjectAccessScope.Read],
|
||||
targetScopes: [TargetAccessScope.Read, TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
targetScopes: [
|
||||
TargetAccessScope.Read,
|
||||
TargetAccessScope.RegistryRead,
|
||||
TargetAccessScope.RegistryWrite,
|
||||
],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
expect(settingsTokenResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -357,7 +372,7 @@ test('normalize and collect operation without breaking its syntax', async () =>
|
|||
to,
|
||||
},
|
||||
},
|
||||
token
|
||||
token,
|
||||
);
|
||||
|
||||
expect(operationStatsResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -389,7 +404,7 @@ test('number of produced and collected operations should match (no errors)', asy
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
|
@ -400,7 +415,7 @@ test('number of produced and collected operations should match (no errors)', asy
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -414,9 +429,13 @@ test('number of produced and collected operations should match (no errors)', asy
|
|||
target: target.cleanId,
|
||||
organizationScopes: [OrganizationAccessScope.Read],
|
||||
projectScopes: [ProjectAccessScope.Read],
|
||||
targetScopes: [TargetAccessScope.Read, TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
targetScopes: [
|
||||
TargetAccessScope.Read,
|
||||
TargetAccessScope.RegistryRead,
|
||||
TargetAccessScope.RegistryWrite,
|
||||
],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
expect(tokenResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -439,7 +458,7 @@ test('number of produced and collected operations should match (no errors)', asy
|
|||
errorsTotal: 0,
|
||||
},
|
||||
},
|
||||
token
|
||||
token,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -457,7 +476,7 @@ test('number of produced and collected operations should match (no errors)', asy
|
|||
to,
|
||||
},
|
||||
},
|
||||
token
|
||||
token,
|
||||
);
|
||||
|
||||
expect(operationStatsResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -487,7 +506,7 @@ test('check usage from two selected targets', async () => {
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
|
@ -498,7 +517,7 @@ test('check usage from two selected targets', async () => {
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -510,7 +529,7 @@ test('check usage from two selected targets', async () => {
|
|||
organization: org.cleanId,
|
||||
project: project.cleanId,
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const production = productionTargetResult.body.data!.createTarget.ok!.createdTarget;
|
||||
|
|
@ -523,9 +542,13 @@ test('check usage from two selected targets', async () => {
|
|||
target: staging.cleanId,
|
||||
organizationScopes: [OrganizationAccessScope.Read],
|
||||
projectScopes: [ProjectAccessScope.Read],
|
||||
targetScopes: [TargetAccessScope.Read, TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
targetScopes: [
|
||||
TargetAccessScope.Read,
|
||||
TargetAccessScope.RegistryRead,
|
||||
TargetAccessScope.RegistryWrite,
|
||||
],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const productionTokenResult = await createToken(
|
||||
|
|
@ -536,9 +559,13 @@ test('check usage from two selected targets', async () => {
|
|||
target: production.cleanId,
|
||||
organizationScopes: [OrganizationAccessScope.Read],
|
||||
projectScopes: [ProjectAccessScope.Read],
|
||||
targetScopes: [TargetAccessScope.Read, TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
targetScopes: [
|
||||
TargetAccessScope.Read,
|
||||
TargetAccessScope.RegistryRead,
|
||||
TargetAccessScope.RegistryWrite,
|
||||
],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
expect(stagingTokenResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -553,7 +580,7 @@ test('check usage from two selected targets', async () => {
|
|||
commit: 'usage-check-2',
|
||||
sdl: `type Query { ping: String me: String }`,
|
||||
},
|
||||
tokenForStaging
|
||||
tokenForStaging,
|
||||
);
|
||||
|
||||
expect(schemaPublishResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -568,7 +595,7 @@ test('check usage from two selected targets', async () => {
|
|||
},
|
||||
{
|
||||
authToken: owner_access_token,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
expect(targetValidationResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -625,7 +652,7 @@ test('check usage from two selected targets', async () => {
|
|||
{
|
||||
sdl: `type Query { me: String }`, // ping is used but on production
|
||||
},
|
||||
tokenForStaging
|
||||
tokenForStaging,
|
||||
);
|
||||
expect(unusedCheckResult.body.errors).not.toBeDefined();
|
||||
expect(unusedCheckResult.body.data!.schemaCheck.__typename).toEqual('SchemaCheckSuccess');
|
||||
|
|
@ -643,19 +670,22 @@ test('check usage from two selected targets', async () => {
|
|||
},
|
||||
{
|
||||
authToken: owner_access_token,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
expect(updateValidationResult.body.errors).not.toBeDefined();
|
||||
expect(updateValidationResult.body.data!.updateTargetValidationSettings.error).toBeNull();
|
||||
expect(
|
||||
updateValidationResult.body.data!.updateTargetValidationSettings.ok!.updatedTargetValidationSettings.percentage
|
||||
updateValidationResult.body.data!.updateTargetValidationSettings.ok!
|
||||
.updatedTargetValidationSettings.percentage,
|
||||
).toEqual(50);
|
||||
expect(
|
||||
updateValidationResult.body.data!.updateTargetValidationSettings.ok!.updatedTargetValidationSettings.period
|
||||
updateValidationResult.body.data!.updateTargetValidationSettings.ok!
|
||||
.updatedTargetValidationSettings.period,
|
||||
).toEqual(2);
|
||||
expect(
|
||||
updateValidationResult.body.data!.updateTargetValidationSettings.ok!.updatedTargetValidationSettings.targets
|
||||
updateValidationResult.body.data!.updateTargetValidationSettings.ok!
|
||||
.updatedTargetValidationSettings.targets,
|
||||
).toHaveLength(2);
|
||||
|
||||
// should be non-breaking because the field is used in production and we are checking staging and production now
|
||||
|
|
@ -664,11 +694,13 @@ test('check usage from two selected targets', async () => {
|
|||
{
|
||||
sdl: `type Query { me: String }`, // ping is used on production and we do check production now
|
||||
},
|
||||
tokenForStaging
|
||||
tokenForStaging,
|
||||
);
|
||||
|
||||
if (usedCheckResult.body.data!.schemaCheck.__typename !== 'SchemaCheckSuccess') {
|
||||
throw new Error(`Expected SchemaCheckSuccess, got ${usedCheckResult.body.data!.schemaCheck.__typename}`);
|
||||
throw new Error(
|
||||
`Expected SchemaCheckSuccess, got ${usedCheckResult.body.data!.schemaCheck.__typename}`,
|
||||
);
|
||||
}
|
||||
|
||||
expect(usedCheckResult.body.data!.schemaCheck.valid).toEqual(true);
|
||||
|
|
@ -681,7 +713,7 @@ test('check usage not from excluded client names', async () => {
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
|
@ -692,11 +724,13 @@ test('check usage not from excluded client names', async () => {
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
const production = projectResult.body.data!.createProject.ok!.createdTargets.find(t => t.name === 'production');
|
||||
const production = projectResult.body.data!.createProject.ok!.createdTargets.find(
|
||||
t => t.name === 'production',
|
||||
);
|
||||
|
||||
if (!production) {
|
||||
throw new Error('No production target');
|
||||
|
|
@ -710,9 +744,13 @@ test('check usage not from excluded client names', async () => {
|
|||
target: production.cleanId,
|
||||
organizationScopes: [OrganizationAccessScope.Read],
|
||||
projectScopes: [ProjectAccessScope.Read],
|
||||
targetScopes: [TargetAccessScope.Read, TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
targetScopes: [
|
||||
TargetAccessScope.Read,
|
||||
TargetAccessScope.RegistryRead,
|
||||
TargetAccessScope.RegistryWrite,
|
||||
],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
expect(productionTokenResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -725,7 +763,7 @@ test('check usage not from excluded client names', async () => {
|
|||
commit: 'usage-check-2',
|
||||
sdl: `type Query { ping: String me: String }`,
|
||||
},
|
||||
tokenForProduction
|
||||
tokenForProduction,
|
||||
);
|
||||
|
||||
expect(schemaPublishResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -740,7 +778,7 @@ test('check usage not from excluded client names', async () => {
|
|||
},
|
||||
{
|
||||
authToken: owner_access_token,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
expect(targetValidationResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -814,7 +852,7 @@ test('check usage not from excluded client names', async () => {
|
|||
{
|
||||
sdl: `type Query { ping: String }`, // Query.me is used
|
||||
},
|
||||
tokenForProduction
|
||||
tokenForProduction,
|
||||
);
|
||||
expect(unusedCheckResult.body.errors).not.toBeDefined();
|
||||
expect(unusedCheckResult.body.data!.schemaCheck.__typename).toEqual('SchemaCheckError');
|
||||
|
|
@ -832,19 +870,22 @@ test('check usage not from excluded client names', async () => {
|
|||
},
|
||||
{
|
||||
authToken: owner_access_token,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
expect(updateValidationResult.body.errors).not.toBeDefined();
|
||||
expect(updateValidationResult.body.data!.updateTargetValidationSettings.error).toBeNull();
|
||||
expect(
|
||||
updateValidationResult.body.data!.updateTargetValidationSettings.ok!.updatedTargetValidationSettings.enabled
|
||||
updateValidationResult.body.data!.updateTargetValidationSettings.ok!
|
||||
.updatedTargetValidationSettings.enabled,
|
||||
).toBe(true);
|
||||
expect(
|
||||
updateValidationResult.body.data!.updateTargetValidationSettings.ok!.updatedTargetValidationSettings.excludedClients
|
||||
updateValidationResult.body.data!.updateTargetValidationSettings.ok!
|
||||
.updatedTargetValidationSettings.excludedClients,
|
||||
).toHaveLength(1);
|
||||
expect(
|
||||
updateValidationResult.body.data!.updateTargetValidationSettings.ok!.updatedTargetValidationSettings.excludedClients
|
||||
updateValidationResult.body.data!.updateTargetValidationSettings.ok!
|
||||
.updatedTargetValidationSettings.excludedClients,
|
||||
).toContainEqual('app');
|
||||
|
||||
// should be safe because the field was not used by the non-excluded clients (cli never requested `Query.me`, but app did)
|
||||
|
|
@ -852,11 +893,13 @@ test('check usage not from excluded client names', async () => {
|
|||
{
|
||||
sdl: `type Query { ping: String }`,
|
||||
},
|
||||
tokenForProduction
|
||||
tokenForProduction,
|
||||
);
|
||||
|
||||
if (usedCheckResult.body.data!.schemaCheck.__typename !== 'SchemaCheckSuccess') {
|
||||
throw new Error(`Expected SchemaCheckSuccess, got ${usedCheckResult.body.data!.schemaCheck.__typename}`);
|
||||
throw new Error(
|
||||
`Expected SchemaCheckSuccess, got ${usedCheckResult.body.data!.schemaCheck.__typename}`,
|
||||
);
|
||||
}
|
||||
|
||||
expect(usedCheckResult.body.data!.schemaCheck.valid).toEqual(true);
|
||||
|
|
@ -869,7 +912,7 @@ test('number of produced and collected operations should match', async () => {
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
|
@ -880,7 +923,7 @@ test('number of produced and collected operations should match', async () => {
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -894,9 +937,13 @@ test('number of produced and collected operations should match', async () => {
|
|||
target: target.cleanId,
|
||||
organizationScopes: [OrganizationAccessScope.Read],
|
||||
projectScopes: [ProjectAccessScope.Read],
|
||||
targetScopes: [TargetAccessScope.Read, TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
targetScopes: [
|
||||
TargetAccessScope.Read,
|
||||
TargetAccessScope.RegistryRead,
|
||||
TargetAccessScope.RegistryWrite,
|
||||
],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
expect(tokenResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -935,7 +982,7 @@ test('number of produced and collected operations should match', async () => {
|
|||
},
|
||||
},
|
||||
},
|
||||
token
|
||||
token,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -963,7 +1010,7 @@ test('number of produced and collected operations should match', async () => {
|
|||
client_name: 'web',
|
||||
hash: expect.any(String),
|
||||
total: expect.stringMatching('5000'),
|
||||
})
|
||||
}),
|
||||
);
|
||||
expect(result.data).toContainEqual(
|
||||
expect.objectContaining({
|
||||
|
|
@ -971,7 +1018,7 @@ test('number of produced and collected operations should match', async () => {
|
|||
client_name: '',
|
||||
hash: expect.any(String),
|
||||
total: expect.stringMatching('5000'),
|
||||
})
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -981,7 +1028,7 @@ test('different order of schema coordinates should not result in different hash'
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
|
@ -992,7 +1039,7 @@ test('different order of schema coordinates should not result in different hash'
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -1006,9 +1053,13 @@ test('different order of schema coordinates should not result in different hash'
|
|||
target: target.cleanId,
|
||||
organizationScopes: [OrganizationAccessScope.Read],
|
||||
projectScopes: [ProjectAccessScope.Read],
|
||||
targetScopes: [TargetAccessScope.Read, TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
targetScopes: [
|
||||
TargetAccessScope.Read,
|
||||
TargetAccessScope.RegistryRead,
|
||||
TargetAccessScope.RegistryWrite,
|
||||
],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
expect(tokenResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -1064,7 +1115,7 @@ test('different order of schema coordinates should not result in different hash'
|
|||
}>(
|
||||
FF_CLICKHOUSE_V2_TABLES
|
||||
? `SELECT hash FROM operation_collection GROUP BY hash`
|
||||
: `SELECT hash FROM operations_registry FINAL GROUP BY hash`
|
||||
: `SELECT hash FROM operations_registry FINAL GROUP BY hash`,
|
||||
);
|
||||
|
||||
expect(operationCollectionResult.rows).toEqual(1);
|
||||
|
|
@ -1076,7 +1127,7 @@ test('same operation but with different schema coordinates should result in diff
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
|
@ -1087,7 +1138,7 @@ test('same operation but with different schema coordinates should result in diff
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -1101,9 +1152,13 @@ test('same operation but with different schema coordinates should result in diff
|
|||
target: target.cleanId,
|
||||
organizationScopes: [OrganizationAccessScope.Read],
|
||||
projectScopes: [ProjectAccessScope.Read],
|
||||
targetScopes: [TargetAccessScope.Read, TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
targetScopes: [
|
||||
TargetAccessScope.Read,
|
||||
TargetAccessScope.RegistryRead,
|
||||
TargetAccessScope.RegistryWrite,
|
||||
],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
expect(tokenResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -1154,7 +1209,7 @@ test('same operation but with different schema coordinates should result in diff
|
|||
}>(
|
||||
FF_CLICKHOUSE_V2_TABLES
|
||||
? `SELECT hash FROM operation_collection GROUP BY hash`
|
||||
: `SELECT hash FROM operations_registry FINAL GROUP BY hash`
|
||||
: `SELECT hash FROM operations_registry FINAL GROUP BY hash`,
|
||||
);
|
||||
|
||||
expect(operationCollectionResult.rows).toEqual(2);
|
||||
|
|
@ -1167,7 +1222,7 @@ test('same operation but with different schema coordinates should result in diff
|
|||
}>(
|
||||
FF_CLICKHOUSE_V2_TABLES
|
||||
? `SELECT hash FROM operation_collection GROUP BY hash`
|
||||
: `SELECT hash FROM operations_registry FINAL GROUP BY hash`
|
||||
: `SELECT hash FROM operations_registry FINAL GROUP BY hash`,
|
||||
);
|
||||
|
||||
expect(operationsResult.rows).toEqual(2);
|
||||
|
|
@ -1179,7 +1234,7 @@ test('operations with the same schema coordinates and body but with different na
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
|
@ -1190,7 +1245,7 @@ test('operations with the same schema coordinates and body but with different na
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -1204,9 +1259,13 @@ test('operations with the same schema coordinates and body but with different na
|
|||
target: target.cleanId,
|
||||
organizationScopes: [OrganizationAccessScope.Read],
|
||||
projectScopes: [ProjectAccessScope.Read],
|
||||
targetScopes: [TargetAccessScope.Read, TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
targetScopes: [
|
||||
TargetAccessScope.Read,
|
||||
TargetAccessScope.RegistryRead,
|
||||
TargetAccessScope.RegistryWrite,
|
||||
],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
expect(tokenResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -1262,7 +1321,7 @@ test('operations with the same schema coordinates and body but with different na
|
|||
}>(
|
||||
FF_CLICKHOUSE_V2_TABLES
|
||||
? `SELECT hash FROM operation_collection GROUP BY hash`
|
||||
: `SELECT hash FROM operations_registry FINAL GROUP BY hash`
|
||||
: `SELECT hash FROM operations_registry FINAL GROUP BY hash`,
|
||||
);
|
||||
|
||||
expect(operationsResult.rows).toEqual(2);
|
||||
|
|
@ -1274,7 +1333,7 @@ test('ignore operations with syntax errors', async () => {
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
|
@ -1285,7 +1344,7 @@ test('ignore operations with syntax errors', async () => {
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -1299,9 +1358,13 @@ test('ignore operations with syntax errors', async () => {
|
|||
target: target.cleanId,
|
||||
organizationScopes: [OrganizationAccessScope.Read],
|
||||
projectScopes: [ProjectAccessScope.Read],
|
||||
targetScopes: [TargetAccessScope.Read, TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
targetScopes: [
|
||||
TargetAccessScope.Read,
|
||||
TargetAccessScope.RegistryRead,
|
||||
TargetAccessScope.RegistryWrite,
|
||||
],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
expect(tokenResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -1339,7 +1402,7 @@ test('ignore operations with syntax errors', async () => {
|
|||
expect.objectContaining({
|
||||
rejected: 1,
|
||||
accepted: 1,
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
await waitFor(5_000);
|
||||
|
|
@ -1365,7 +1428,7 @@ test('ignore operations with syntax errors', async () => {
|
|||
}>(
|
||||
FF_CLICKHOUSE_V2_TABLES
|
||||
? `SELECT hash FROM operation_collection GROUP BY hash`
|
||||
: `SELECT hash FROM operations_registry FINAL GROUP BY hash`
|
||||
: `SELECT hash FROM operations_registry FINAL GROUP BY hash`,
|
||||
);
|
||||
|
||||
expect(operationsResult.rows).toEqual(1);
|
||||
|
|
@ -1377,7 +1440,7 @@ test('ensure correct data', async () => {
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
|
@ -1388,7 +1451,7 @@ test('ensure correct data', async () => {
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -1402,9 +1465,13 @@ test('ensure correct data', async () => {
|
|||
target: target.cleanId,
|
||||
organizationScopes: [OrganizationAccessScope.Read],
|
||||
projectScopes: [ProjectAccessScope.Read],
|
||||
targetScopes: [TargetAccessScope.Read, TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
targetScopes: [
|
||||
TargetAccessScope.Read,
|
||||
TargetAccessScope.RegistryRead,
|
||||
TargetAccessScope.RegistryWrite,
|
||||
],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
expect(tokenResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -1637,7 +1704,9 @@ test('ensure correct data', async () => {
|
|||
expect(dailyAggOfKnownClient.hash).toHaveLength(32);
|
||||
expect(dailyAggOfKnownClient.target).toEqual(target.id);
|
||||
|
||||
const dailyAggOfUnknownClient = clientsDailyResult.data.find(c => c.client_name !== 'test-name')!;
|
||||
const dailyAggOfUnknownClient = clientsDailyResult.data.find(
|
||||
c => c.client_name !== 'test-name',
|
||||
)!;
|
||||
expect(dailyAggOfUnknownClient).toBeDefined();
|
||||
expect(ensureNumber(dailyAggOfUnknownClient.total)).toEqual(1);
|
||||
expect(dailyAggOfUnknownClient.client_version).toHaveLength(0);
|
||||
|
|
@ -1799,7 +1868,9 @@ test('ensure correct data', async () => {
|
|||
expect(dailyAggOfKnownClient.hash).toHaveLength(32);
|
||||
expect(dailyAggOfKnownClient.target).toEqual(target.id);
|
||||
|
||||
const dailyAggOfUnknownClient = clientsDailyResult.data.find(c => c.client_name !== 'test-name')!;
|
||||
const dailyAggOfUnknownClient = clientsDailyResult.data.find(
|
||||
c => c.client_name !== 'test-name',
|
||||
)!;
|
||||
expect(dailyAggOfUnknownClient).toBeDefined();
|
||||
expect(ensureNumber(dailyAggOfUnknownClient.total)).toEqual(1);
|
||||
expect(dailyAggOfUnknownClient.hash).toHaveLength(32);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
import { ProjectType } from '@app/gql/graphql';
|
||||
import { createOrganization, createProject, createToken, readTokenInfo, deleteTokens } from '../../testkit/flow';
|
||||
import {
|
||||
createOrganization,
|
||||
createProject,
|
||||
createToken,
|
||||
readTokenInfo,
|
||||
deleteTokens,
|
||||
} from '../../testkit/flow';
|
||||
import { authenticate } from '../../testkit/auth';
|
||||
|
||||
test('deleting a token should clear the cache', async () => {
|
||||
|
|
@ -8,7 +14,7 @@ test('deleting a token should clear the cache', async () => {
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
|
@ -19,7 +25,7 @@ test('deleting a token should clear the cache', async () => {
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -36,7 +42,7 @@ test('deleting a token should clear the cache', async () => {
|
|||
projectScopes: [],
|
||||
targetScopes: [],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
expect(tokenResult.body.errors).not.toBeDefined();
|
||||
|
|
@ -83,7 +89,7 @@ test('deleting a token should clear the cache', async () => {
|
|||
target: target.cleanId,
|
||||
tokens: [createdToken.id],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
tokenInfoResult = await readTokenInfo(secret!);
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ test('can publish and check a schema with target:registry:read access', async ()
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
||||
|
|
@ -26,7 +26,7 @@ test('can publish and check a schema with target:registry:read access', async ()
|
|||
email: 'some@email.com',
|
||||
organization: org.cleanId,
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const inviteCode = invitationResult.body.data?.inviteToOrganizationByEmail.ok?.code;
|
||||
|
|
@ -42,7 +42,7 @@ test('can publish and check a schema with target:registry:read access', async ()
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -59,7 +59,7 @@ test('can publish and check a schema with target:registry:read access', async ()
|
|||
projectScopes: [],
|
||||
targetScopes: [TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
expect(writeTokenResult.body.errors).not.toBeDefined();
|
||||
const writeToken = writeTokenResult.body.data!.createToken.ok!.secret;
|
||||
|
|
@ -76,9 +76,9 @@ test('can publish and check a schema with target:registry:read access', async ()
|
|||
|
||||
await schemaCheck(['--token', writeToken, 'fixtures/nonbreaking-schema.graphql']);
|
||||
|
||||
await expect(schemaCheck(['--token', writeToken, 'fixtures/breaking-schema.graphql'])).rejects.toThrowError(
|
||||
/breaking/
|
||||
);
|
||||
await expect(
|
||||
schemaCheck(['--token', writeToken, 'fixtures/breaking-schema.graphql']),
|
||||
).rejects.toThrowError(/breaking/);
|
||||
});
|
||||
|
||||
test('publishing a breaking change results in invalid state', async () => {
|
||||
|
|
@ -87,7 +87,7 @@ test('publishing a breaking change results in invalid state', async () => {
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
||||
|
|
@ -96,7 +96,7 @@ test('publishing a breaking change results in invalid state', async () => {
|
|||
email: 'some@email.com',
|
||||
organization: org.cleanId,
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const inviteCode = invitationResult.body.data?.inviteToOrganizationByEmail.ok?.code;
|
||||
|
|
@ -112,7 +112,7 @@ test('publishing a breaking change results in invalid state', async () => {
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -129,7 +129,7 @@ test('publishing a breaking change results in invalid state', async () => {
|
|||
projectScopes: [],
|
||||
targetScopes: [TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
expect(writeTokenResult.body.errors).not.toBeDefined();
|
||||
const writeToken = writeTokenResult.body.data!.createToken.ok!.secret;
|
||||
|
|
@ -144,9 +144,9 @@ test('publishing a breaking change results in invalid state', async () => {
|
|||
'fixtures/init-schema.graphql',
|
||||
]);
|
||||
|
||||
await expect(schemaPublish(['--token', writeToken, 'fixtures/breaking-schema.graphql'])).rejects.toThrowError(
|
||||
/breaking/
|
||||
);
|
||||
await expect(
|
||||
schemaPublish(['--token', writeToken, 'fixtures/breaking-schema.graphql']),
|
||||
).rejects.toThrowError(/breaking/);
|
||||
});
|
||||
|
||||
test('publishing invalid schema SDL provides meaningful feedback for the user.', async () => {
|
||||
|
|
@ -155,7 +155,7 @@ test('publishing invalid schema SDL provides meaningful feedback for the user.',
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
||||
|
|
@ -164,7 +164,7 @@ test('publishing invalid schema SDL provides meaningful feedback for the user.',
|
|||
email: 'some@email.com',
|
||||
organization: org.cleanId,
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const code = invitationResult.body.data?.inviteToOrganizationByEmail.ok?.code;
|
||||
|
|
@ -180,7 +180,7 @@ test('publishing invalid schema SDL provides meaningful feedback for the user.',
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -197,7 +197,7 @@ test('publishing invalid schema SDL provides meaningful feedback for the user.',
|
|||
projectScopes: [],
|
||||
targetScopes: [TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
expect(writeTokenResult.body.errors).not.toBeDefined();
|
||||
const writeToken = writeTokenResult.body.data!.createToken.ok!.secret;
|
||||
|
|
@ -229,7 +229,7 @@ test('service url should be available in supergraph', async () => {
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
||||
|
|
@ -238,7 +238,7 @@ test('service url should be available in supergraph', async () => {
|
|||
email: 'some@email.com',
|
||||
organization: org.cleanId,
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const code = invitationResult.body.data?.inviteToOrganizationByEmail.ok?.code;
|
||||
|
|
@ -254,7 +254,7 @@ test('service url should be available in supergraph', async () => {
|
|||
type: ProjectType.Federation,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -271,7 +271,7 @@ test('service url should be available in supergraph', async () => {
|
|||
projectScopes: [],
|
||||
targetScopes: [TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
expect(writeTokenResult.body.errors).not.toBeDefined();
|
||||
const writeToken = writeTokenResult.body.data!.createToken.ok!.secret;
|
||||
|
|
@ -296,7 +296,7 @@ test('service url should be available in supergraph', async () => {
|
|||
project: project.cleanId,
|
||||
target: target.cleanId,
|
||||
},
|
||||
writeToken
|
||||
writeToken,
|
||||
);
|
||||
|
||||
expect(supergraph.body).toMatch('(name: "users" url: "https://api.com/users-subgraph")');
|
||||
|
|
@ -308,7 +308,7 @@ test('service url should be required in Federation', async () => {
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
||||
|
|
@ -317,7 +317,7 @@ test('service url should be required in Federation', async () => {
|
|||
email: 'some@email.com',
|
||||
organization: org.cleanId,
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const code = invitationResult.body.data?.inviteToOrganizationByEmail.ok?.code;
|
||||
|
|
@ -333,7 +333,7 @@ test('service url should be required in Federation', async () => {
|
|||
type: ProjectType.Federation,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -350,7 +350,7 @@ test('service url should be required in Federation', async () => {
|
|||
projectScopes: [],
|
||||
targetScopes: [TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
expect(writeTokenResult.body.errors).not.toBeDefined();
|
||||
const writeToken = writeTokenResult.body.data!.createToken.ok!.secret;
|
||||
|
|
@ -366,7 +366,7 @@ test('service url should be required in Federation', async () => {
|
|||
'--service',
|
||||
'users',
|
||||
'fixtures/federation-init.graphql',
|
||||
])
|
||||
]),
|
||||
).rejects.toThrowError(/url/);
|
||||
});
|
||||
|
||||
|
|
@ -376,7 +376,7 @@ test('schema:publish should print a link to the website', async () => {
|
|||
{
|
||||
name: 'bar',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
||||
|
|
@ -385,7 +385,7 @@ test('schema:publish should print a link to the website', async () => {
|
|||
email: 'some@email.com',
|
||||
organization: org.cleanId,
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const code = invitationResult.body.data?.inviteToOrganizationByEmail.ok?.code;
|
||||
|
|
@ -401,7 +401,7 @@ test('schema:publish should print a link to the website', async () => {
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -419,18 +419,18 @@ test('schema:publish should print a link to the website', async () => {
|
|||
projectScopes: [],
|
||||
targetScopes: [TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
expect(writeTokenResult.body.errors).not.toBeDefined();
|
||||
const writeToken = writeTokenResult.body.data!.createToken.ok!.secret;
|
||||
|
||||
await expect(schemaPublish(['--token', writeToken, 'fixtures/init-schema.graphql'])).resolves.toMatch(
|
||||
'Available at https://app.graphql-hive.com/bar/foo/development'
|
||||
);
|
||||
await expect(
|
||||
schemaPublish(['--token', writeToken, 'fixtures/init-schema.graphql']),
|
||||
).resolves.toMatch('Available at https://app.graphql-hive.com/bar/foo/development');
|
||||
|
||||
await expect(schemaPublish(['--token', writeToken, 'fixtures/nonbreaking-schema.graphql'])).resolves.toMatch(
|
||||
'Available at https://app.graphql-hive.com/bar/foo/development/history/'
|
||||
);
|
||||
await expect(
|
||||
schemaPublish(['--token', writeToken, 'fixtures/nonbreaking-schema.graphql']),
|
||||
).resolves.toMatch('Available at https://app.graphql-hive.com/bar/foo/development/history/');
|
||||
});
|
||||
|
||||
test('schema:check should notify user when registry is empty', async () => {
|
||||
|
|
@ -439,7 +439,7 @@ test('schema:check should notify user when registry is empty', async () => {
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
||||
|
|
@ -448,7 +448,7 @@ test('schema:check should notify user when registry is empty', async () => {
|
|||
email: 'some@email.com',
|
||||
organization: org.cleanId,
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const code = invitationResult.body.data?.inviteToOrganizationByEmail.ok?.code;
|
||||
|
|
@ -464,7 +464,7 @@ test('schema:check should notify user when registry is empty', async () => {
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -481,12 +481,14 @@ test('schema:check should notify user when registry is empty', async () => {
|
|||
projectScopes: [],
|
||||
targetScopes: [TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
expect(writeTokenResult.body.errors).not.toBeDefined();
|
||||
const writeToken = writeTokenResult.body.data!.createToken.ok!.secret;
|
||||
|
||||
await expect(schemaCheck(['--token', writeToken, 'fixtures/init-schema.graphql'])).resolves.toMatch('empty');
|
||||
await expect(
|
||||
schemaCheck(['--token', writeToken, 'fixtures/init-schema.graphql']),
|
||||
).resolves.toMatch('empty');
|
||||
});
|
||||
|
||||
test('schema:check should throw on corrupted schema', async () => {
|
||||
|
|
@ -495,7 +497,7 @@ test('schema:check should throw on corrupted schema', async () => {
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
||||
|
|
@ -504,7 +506,7 @@ test('schema:check should throw on corrupted schema', async () => {
|
|||
email: 'some@email.com',
|
||||
organization: org.cleanId,
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const code = invitationResult.body.data?.inviteToOrganizationByEmail.ok?.code;
|
||||
|
|
@ -520,7 +522,7 @@ test('schema:check should throw on corrupted schema', async () => {
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -537,7 +539,7 @@ test('schema:check should throw on corrupted schema', async () => {
|
|||
projectScopes: [],
|
||||
targetScopes: [TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
expect(writeTokenResult.body.errors).not.toBeDefined();
|
||||
const writeToken = writeTokenResult.body.data!.createToken.ok!.secret;
|
||||
|
|
@ -553,7 +555,7 @@ test('schema:publish should see Invalid Token error when token is invalid', asyn
|
|||
{
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
|
||||
|
|
@ -563,7 +565,7 @@ test('schema:publish should see Invalid Token error when token is invalid', asyn
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const token = createHash('md5').update('nope').digest('hex').substring(0, 31);
|
||||
|
|
@ -579,7 +581,7 @@ test('schema:publish should support experimental_acceptBreakingChanges flag', as
|
|||
{
|
||||
name: 'bar',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
|
||||
const projectResult = await createProject(
|
||||
|
|
@ -588,7 +590,7 @@ test('schema:publish should support experimental_acceptBreakingChanges flag', as
|
|||
type: ProjectType.Single,
|
||||
name: 'foo',
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
|
||||
const project = projectResult.body.data!.createProject.ok!.createdProject;
|
||||
|
|
@ -606,16 +608,21 @@ test('schema:publish should support experimental_acceptBreakingChanges flag', as
|
|||
projectScopes: [],
|
||||
targetScopes: [TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
|
||||
},
|
||||
owner_access_token
|
||||
owner_access_token,
|
||||
);
|
||||
expect(writeTokenResult.body.errors).not.toBeDefined();
|
||||
const writeToken = writeTokenResult.body.data!.createToken.ok!.secret;
|
||||
|
||||
await expect(schemaPublish(['--token', writeToken, 'fixtures/init-schema.graphql'])).resolves.toMatch(
|
||||
'Available at https://app.graphql-hive.com/bar/foo/development'
|
||||
);
|
||||
await expect(
|
||||
schemaPublish(['--token', writeToken, 'fixtures/init-schema.graphql']),
|
||||
).resolves.toMatch('Available at https://app.graphql-hive.com/bar/foo/development');
|
||||
|
||||
await expect(
|
||||
schemaPublish(['--token', writeToken, '--experimental_acceptBreakingChanges', 'fixtures/breaking-schema.graphql'])
|
||||
schemaPublish([
|
||||
'--token',
|
||||
writeToken,
|
||||
'--experimental_acceptBreakingChanges',
|
||||
'fixtures/breaking-schema.graphql',
|
||||
]),
|
||||
).resolves.toMatch('Available at https://app.graphql-hive.com/bar/foo/development/history/');
|
||||
});
|
||||
|
|
|
|||
50
package.json
50
package.json
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "graphql-hive",
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "kamilkisiela/graphql-hive"
|
||||
|
|
@ -12,29 +12,35 @@
|
|||
"url": "https://the-guild.dev"
|
||||
},
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
"packageManager": "pnpm@7.14.2",
|
||||
"engines": {
|
||||
"node": ">=16",
|
||||
"pnpm": ">=7"
|
||||
},
|
||||
"scripts": {
|
||||
"seed": "node scripts/seed-local-env.js",
|
||||
"build": "pnpm graphql:generate && pnpm turbo build --color",
|
||||
"build:libraries": "pnpm graphql:generate && pnpm turbo build --filter=./packages/libraries/* --color",
|
||||
"build:local": "pnpm graphql:generate && pnpm turbo build-local --color",
|
||||
"build:services": "pnpm graphql:generate && pnpm turbo build --filter=./packages/services/* --color",
|
||||
"build:web": "pnpm graphql:generate && pnpm turbo build --filter=./packages/web/* --color",
|
||||
"docker:build": "docker buildx bake -f docker.hcl --load build",
|
||||
"env:sync": "node ./scripts/sync-env-files.js",
|
||||
"generate": "pnpm --filter @hive/storage db:generate && pnpm graphql:generate",
|
||||
"graphql:generate": "graphql-codegen",
|
||||
"lint": "eslint --cache --ignore-path .gitignore \"packages/**/*.{ts,tsx}\"",
|
||||
"lint:prettier": "prettier --cache --check .",
|
||||
"postinstall": "husky install && node ./scripts/patch-manifests.js && pnpm env:sync && node ./scripts/turborepo-cleanup.js && node ./scripts/turborepo-setup.js",
|
||||
"pre-commit": "lint-staged",
|
||||
"prerelease": "pnpm build:libraries",
|
||||
"release": "changeset publish",
|
||||
"upload-sourcemaps": "./scripts/upload-sourcemaps.sh",
|
||||
"test": "jest",
|
||||
"lint": "eslint --cache --ignore-path .gitignore \"packages/**/*.{ts,tsx}\"",
|
||||
"prettier": "prettier --cache --write --list-different .",
|
||||
"lint:prettier": "prettier --cache --check .",
|
||||
"release": "changeset publish",
|
||||
"seed": "node scripts/seed-local-env.js",
|
||||
"setup": "pnpm --filter @hive/storage setup",
|
||||
"generate": "pnpm --filter @hive/storage db:generate && pnpm graphql:generate",
|
||||
"graphql:generate": "graphql-codegen",
|
||||
"typecheck": "pnpm turbo typecheck --color",
|
||||
"build": "pnpm graphql:generate && pnpm turbo build --color",
|
||||
"build:libraries": "pnpm graphql:generate && pnpm turbo build --filter=./packages/libraries/* --color",
|
||||
"build:web": "pnpm graphql:generate && pnpm turbo build --filter=./packages/web/* --color",
|
||||
"build:services": "pnpm graphql:generate && pnpm turbo build --filter=./packages/services/* --color",
|
||||
"build:local": "pnpm graphql:generate && pnpm turbo build-local --color",
|
||||
"env:sync": "node ./scripts/sync-env-files.js",
|
||||
"test": "jest",
|
||||
"turbo": "env-cmd --silent turbo run",
|
||||
"docker:build": "docker buildx bake -f docker.hcl --load build"
|
||||
"typecheck": "pnpm turbo typecheck --color",
|
||||
"upload-sourcemaps": "./scripts/upload-sourcemaps.sh"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.19.1",
|
||||
|
|
@ -55,7 +61,7 @@
|
|||
"@graphql-codegen/typescript-resolvers": "2.7.5",
|
||||
"@sentry/cli": "2.9.0",
|
||||
"@swc/core": "1.2.185",
|
||||
"@theguild/prettier-config": "0.1.1",
|
||||
"@theguild/prettier-config": "1.0.0",
|
||||
"@types/jest": "29.2.0",
|
||||
"@types/lru-cache": "7.6.1",
|
||||
"@types/node": "16.11.22",
|
||||
|
|
@ -106,11 +112,6 @@
|
|||
"eslint --fix"
|
||||
]
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16",
|
||||
"pnpm": ">=7"
|
||||
},
|
||||
"packageManager": "pnpm@7.14.2",
|
||||
"pnpm": {
|
||||
"patchedDependencies": {
|
||||
"@n1ru4l/dockest@2.1.0-rc.6": "patches/@n1ru4l__dockest@2.1.0-rc.6.patch",
|
||||
|
|
@ -123,6 +124,5 @@
|
|||
"bob-the-bundler@4.0.0": "patches/bob-the-bundler@4.0.0.patch",
|
||||
"oclif@3.2.25": "patches/oclif@3.2.25.patch"
|
||||
}
|
||||
},
|
||||
"version": "0.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,76 +4,102 @@
|
|||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`e116841`](https://github.com/kamilkisiela/graphql-hive/commit/e116841a739bfd7f37c4a826544301cf23d61637)]:
|
||||
- Updated dependencies
|
||||
[[`e116841`](https://github.com/kamilkisiela/graphql-hive/commit/e116841a739bfd7f37c4a826544301cf23d61637)]:
|
||||
- @graphql-hive/core@0.2.3
|
||||
|
||||
## 0.19.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#655](https://github.com/kamilkisiela/graphql-hive/pull/655) [`2cbf27f`](https://github.com/kamilkisiela/graphql-hive/commit/2cbf27fdc9c18749b8969adb6d1598338762dba2) Thanks [@n1ru4l](https://github.com/n1ru4l)! - Add User-Agent header to all http requests
|
||||
- [#655](https://github.com/kamilkisiela/graphql-hive/pull/655)
|
||||
[`2cbf27f`](https://github.com/kamilkisiela/graphql-hive/commit/2cbf27fdc9c18749b8969adb6d1598338762dba2)
|
||||
Thanks [@n1ru4l](https://github.com/n1ru4l)! - Add User-Agent header to all http requests
|
||||
|
||||
## 0.19.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#648](https://github.com/kamilkisiela/graphql-hive/pull/648) [`84a78fc`](https://github.com/kamilkisiela/graphql-hive/commit/84a78fc2a4061e05b1bbe4a8d11006601c767384) Thanks [@kamilkisiela](https://github.com/kamilkisiela)! - bump
|
||||
- [#648](https://github.com/kamilkisiela/graphql-hive/pull/648)
|
||||
[`84a78fc`](https://github.com/kamilkisiela/graphql-hive/commit/84a78fc2a4061e05b1bbe4a8d11006601c767384)
|
||||
Thanks [@kamilkisiela](https://github.com/kamilkisiela)! - bump
|
||||
|
||||
## 0.19.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#646](https://github.com/kamilkisiela/graphql-hive/pull/646) [`65f3372`](https://github.com/kamilkisiela/graphql-hive/commit/65f3372dfa047238352beee113ccb8506cc180ca) Thanks [@kamilkisiela](https://github.com/kamilkisiela)! - I hope it's final bump
|
||||
- [#646](https://github.com/kamilkisiela/graphql-hive/pull/646)
|
||||
[`65f3372`](https://github.com/kamilkisiela/graphql-hive/commit/65f3372dfa047238352beee113ccb8506cc180ca)
|
||||
Thanks [@kamilkisiela](https://github.com/kamilkisiela)! - I hope it's final bump
|
||||
|
||||
## 0.19.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#645](https://github.com/kamilkisiela/graphql-hive/pull/645) [`7110555`](https://github.com/kamilkisiela/graphql-hive/commit/71105559b67f510087223ada2af23564ff053353) Thanks [@kamilkisiela](https://github.com/kamilkisiela)! - Ignore npm-shrinkwrap.json
|
||||
- [#645](https://github.com/kamilkisiela/graphql-hive/pull/645)
|
||||
[`7110555`](https://github.com/kamilkisiela/graphql-hive/commit/71105559b67f510087223ada2af23564ff053353)
|
||||
Thanks [@kamilkisiela](https://github.com/kamilkisiela)! - Ignore npm-shrinkwrap.json
|
||||
|
||||
## 0.19.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#641](https://github.com/kamilkisiela/graphql-hive/pull/641) [`ce55b72`](https://github.com/kamilkisiela/graphql-hive/commit/ce55b724b00ff7fc93f3df4089e698e6f9d5086b) Thanks [@kamilkisiela](https://github.com/kamilkisiela)! - Do not include npm-shrinkwrap.json
|
||||
- [#641](https://github.com/kamilkisiela/graphql-hive/pull/641)
|
||||
[`ce55b72`](https://github.com/kamilkisiela/graphql-hive/commit/ce55b724b00ff7fc93f3df4089e698e6f9d5086b)
|
||||
Thanks [@kamilkisiela](https://github.com/kamilkisiela)! - Do not include npm-shrinkwrap.json
|
||||
|
||||
## 0.19.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#631](https://github.com/kamilkisiela/graphql-hive/pull/631) [`d4ca981`](https://github.com/kamilkisiela/graphql-hive/commit/d4ca98180bd0b2910fb41f623c2f5abb1f4b9214) Thanks [@kamilkisiela](https://github.com/kamilkisiela)! - Bump
|
||||
- [#631](https://github.com/kamilkisiela/graphql-hive/pull/631)
|
||||
[`d4ca981`](https://github.com/kamilkisiela/graphql-hive/commit/d4ca98180bd0b2910fb41f623c2f5abb1f4b9214)
|
||||
Thanks [@kamilkisiela](https://github.com/kamilkisiela)! - Bump
|
||||
|
||||
- [#631](https://github.com/kamilkisiela/graphql-hive/pull/631) [`d4ca981`](https://github.com/kamilkisiela/graphql-hive/commit/d4ca98180bd0b2910fb41f623c2f5abb1f4b9214) Thanks [@kamilkisiela](https://github.com/kamilkisiela)! - Bump CLI
|
||||
- [#631](https://github.com/kamilkisiela/graphql-hive/pull/631)
|
||||
[`d4ca981`](https://github.com/kamilkisiela/graphql-hive/commit/d4ca98180bd0b2910fb41f623c2f5abb1f4b9214)
|
||||
Thanks [@kamilkisiela](https://github.com/kamilkisiela)! - Bump CLI
|
||||
|
||||
## 0.19.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#629](https://github.com/kamilkisiela/graphql-hive/pull/629) [`750b46d`](https://github.com/kamilkisiela/graphql-hive/commit/750b46d155c5d01ad4b3cee84409793736246603) Thanks [@kamilkisiela](https://github.com/kamilkisiela)! - Bump
|
||||
- [#629](https://github.com/kamilkisiela/graphql-hive/pull/629)
|
||||
[`750b46d`](https://github.com/kamilkisiela/graphql-hive/commit/750b46d155c5d01ad4b3cee84409793736246603)
|
||||
Thanks [@kamilkisiela](https://github.com/kamilkisiela)! - Bump
|
||||
|
||||
## 0.19.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#627](https://github.com/kamilkisiela/graphql-hive/pull/627) [`78096dc`](https://github.com/kamilkisiela/graphql-hive/commit/78096dcfbd37059fbb309e8faa6bae1d14e18c79) Thanks [@kamilkisiela](https://github.com/kamilkisiela)! - bump
|
||||
- [#627](https://github.com/kamilkisiela/graphql-hive/pull/627)
|
||||
[`78096dc`](https://github.com/kamilkisiela/graphql-hive/commit/78096dcfbd37059fbb309e8faa6bae1d14e18c79)
|
||||
Thanks [@kamilkisiela](https://github.com/kamilkisiela)! - bump
|
||||
|
||||
## 0.19.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#466](https://github.com/kamilkisiela/graphql-hive/pull/466) [`2e036ac`](https://github.com/kamilkisiela/graphql-hive/commit/2e036acc4ce1c27a493e90481bb10f5886c0a00c) Thanks [@ardatan](https://github.com/ardatan)! - Update GraphQL Tools packages
|
||||
- [#466](https://github.com/kamilkisiela/graphql-hive/pull/466)
|
||||
[`2e036ac`](https://github.com/kamilkisiela/graphql-hive/commit/2e036acc4ce1c27a493e90481bb10f5886c0a00c)
|
||||
Thanks [@ardatan](https://github.com/ardatan)! - Update GraphQL Tools packages
|
||||
|
||||
## 0.19.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#357](https://github.com/kamilkisiela/graphql-hive/pull/357) [`30f11c4`](https://github.com/kamilkisiela/graphql-hive/commit/30f11c40054debfcbd8b6090316d129eb7851046) Thanks [@kamilkisiela](https://github.com/kamilkisiela)! - Adds experimental_acceptBreakingChanges
|
||||
- [#357](https://github.com/kamilkisiela/graphql-hive/pull/357)
|
||||
[`30f11c4`](https://github.com/kamilkisiela/graphql-hive/commit/30f11c40054debfcbd8b6090316d129eb7851046)
|
||||
Thanks [@kamilkisiela](https://github.com/kamilkisiela)! - Adds experimental_acceptBreakingChanges
|
||||
|
||||
## 0.18.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#292](https://github.com/kamilkisiela/graphql-hive/pull/292) [`efb03e1`](https://github.com/kamilkisiela/graphql-hive/commit/efb03e184d5a878dbcca83295b2d1d53b3c9f8e3) Thanks [@kamilkisiela](https://github.com/kamilkisiela)! - Bump @oclif/core dependency range to ^1.13.10
|
||||
- [#292](https://github.com/kamilkisiela/graphql-hive/pull/292)
|
||||
[`efb03e1`](https://github.com/kamilkisiela/graphql-hive/commit/efb03e184d5a878dbcca83295b2d1d53b3c9f8e3)
|
||||
Thanks [@kamilkisiela](https://github.com/kamilkisiela)! - Bump @oclif/core dependency range to
|
||||
^1.13.10
|
||||
|
||||
## 0.18.1
|
||||
|
||||
|
|
@ -187,9 +213,11 @@
|
|||
|
||||
- c5bfa4c9: Add a new `metadata` flag for publishing schema metadata (JSON) to Hive.
|
||||
|
||||
The `--metadata` can contain anything you wish to have attached to your GraphQL schema, and can support your runtime needs.
|
||||
The `--metadata` can contain anything you wish to have attached to your GraphQL schema, and can
|
||||
support your runtime needs.
|
||||
|
||||
You can either specify a path to a file: `--metadata my-file.json`, or an inline JSON object: `--metadata '{"test": 1}'`.
|
||||
You can either specify a path to a file: `--metadata my-file.json`, or an inline JSON object:
|
||||
`--metadata '{"test": 1}'`.
|
||||
|
||||
Metadata published to Hive will be available as part of Hive CDN, under `/metadata` route.
|
||||
|
||||
|
|
@ -403,7 +431,8 @@
|
|||
### Minor Changes
|
||||
|
||||
- 078e758: Token per Target
|
||||
- 1dd9cdb: --file flag is now replaced with a positional arg at the end, comments in graphql sdl files are now converted to descriptions, docs are updated to mention wildcard file uploads
|
||||
- 1dd9cdb: --file flag is now replaced with a positional arg at the end, comments in graphql sdl
|
||||
files are now converted to descriptions, docs are updated to mention wildcard file uploads
|
||||
|
||||
### Patch Changes
|
||||
|
||||
|
|
|
|||
|
|
@ -63,7 +63,8 @@ DESCRIPTION
|
|||
deletes specific cli configuration
|
||||
```
|
||||
|
||||
_See code: [dist/commands/config/delete.js](https://github.com/kamilkisiela/graphql-hive/blob/v0.19.5/dist/commands/config/delete.js)_
|
||||
_See code:
|
||||
[dist/commands/config/delete.js](https://github.com/kamilkisiela/graphql-hive/blob/v0.19.5/dist/commands/config/delete.js)_
|
||||
|
||||
## `hive config:get KEY`
|
||||
|
||||
|
|
@ -80,7 +81,8 @@ DESCRIPTION
|
|||
prints specific cli configuration
|
||||
```
|
||||
|
||||
_See code: [dist/commands/config/get.js](https://github.com/kamilkisiela/graphql-hive/blob/v0.19.5/dist/commands/config/get.js)_
|
||||
_See code:
|
||||
[dist/commands/config/get.js](https://github.com/kamilkisiela/graphql-hive/blob/v0.19.5/dist/commands/config/get.js)_
|
||||
|
||||
## `hive config:reset`
|
||||
|
||||
|
|
@ -94,7 +96,8 @@ DESCRIPTION
|
|||
resets local cli configuration
|
||||
```
|
||||
|
||||
_See code: [dist/commands/config/reset.js](https://github.com/kamilkisiela/graphql-hive/blob/v0.19.5/dist/commands/config/reset.js)_
|
||||
_See code:
|
||||
[dist/commands/config/reset.js](https://github.com/kamilkisiela/graphql-hive/blob/v0.19.5/dist/commands/config/reset.js)_
|
||||
|
||||
## `hive config:set KEY VALUE`
|
||||
|
||||
|
|
@ -112,7 +115,8 @@ DESCRIPTION
|
|||
updates specific cli configuration
|
||||
```
|
||||
|
||||
_See code: [dist/commands/config/set.js](https://github.com/kamilkisiela/graphql-hive/blob/v0.19.5/dist/commands/config/set.js)_
|
||||
_See code:
|
||||
[dist/commands/config/set.js](https://github.com/kamilkisiela/graphql-hive/blob/v0.19.5/dist/commands/config/set.js)_
|
||||
|
||||
## `hive help [COMMAND]`
|
||||
|
||||
|
|
@ -132,7 +136,8 @@ DESCRIPTION
|
|||
Display help for hive.
|
||||
```
|
||||
|
||||
_See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v5.1.18/src/commands/help.ts)_
|
||||
_See code:
|
||||
[@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v5.1.18/src/commands/help.ts)_
|
||||
|
||||
## `hive operations:check FILE`
|
||||
|
||||
|
|
@ -154,7 +159,8 @@ DESCRIPTION
|
|||
checks operations against a published schema
|
||||
```
|
||||
|
||||
_See code: [dist/commands/operations/check.js](https://github.com/kamilkisiela/graphql-hive/blob/v0.19.5/dist/commands/operations/check.js)_
|
||||
_See code:
|
||||
[dist/commands/operations/check.js](https://github.com/kamilkisiela/graphql-hive/blob/v0.19.5/dist/commands/operations/check.js)_
|
||||
|
||||
## `hive operations:publish FILE`
|
||||
|
||||
|
|
@ -177,7 +183,8 @@ DESCRIPTION
|
|||
saves operations to the store
|
||||
```
|
||||
|
||||
_See code: [dist/commands/operations/publish.js](https://github.com/kamilkisiela/graphql-hive/blob/v0.19.5/dist/commands/operations/publish.js)_
|
||||
_See code:
|
||||
[dist/commands/operations/publish.js](https://github.com/kamilkisiela/graphql-hive/blob/v0.19.5/dist/commands/operations/publish.js)_
|
||||
|
||||
## `hive schema:check FILE`
|
||||
|
||||
|
|
@ -204,7 +211,8 @@ DESCRIPTION
|
|||
checks schema
|
||||
```
|
||||
|
||||
_See code: [dist/commands/schema/check.js](https://github.com/kamilkisiela/graphql-hive/blob/v0.19.5/dist/commands/schema/check.js)_
|
||||
_See code:
|
||||
[dist/commands/schema/check.js](https://github.com/kamilkisiela/graphql-hive/blob/v0.19.5/dist/commands/schema/check.js)_
|
||||
|
||||
## `hive schema:publish FILE`
|
||||
|
||||
|
|
@ -239,7 +247,8 @@ DESCRIPTION
|
|||
publishes schema
|
||||
```
|
||||
|
||||
_See code: [dist/commands/schema/publish.js](https://github.com/kamilkisiela/graphql-hive/blob/v0.19.5/dist/commands/schema/publish.js)_
|
||||
_See code:
|
||||
[dist/commands/schema/publish.js](https://github.com/kamilkisiela/graphql-hive/blob/v0.19.5/dist/commands/schema/publish.js)_
|
||||
|
||||
## `hive update [CHANNEL]`
|
||||
|
||||
|
|
@ -276,7 +285,8 @@ EXAMPLES
|
|||
$ hive update --available
|
||||
```
|
||||
|
||||
_See code: [@oclif/plugin-update](https://github.com/oclif/plugin-update/blob/v3.0.6/src/commands/update.ts)_
|
||||
_See code:
|
||||
[@oclif/plugin-update](https://github.com/oclif/plugin-update/blob/v3.0.6/src/commands/update.ts)_
|
||||
|
||||
## `hive whoami`
|
||||
|
||||
|
|
@ -294,7 +304,8 @@ DESCRIPTION
|
|||
checks schema
|
||||
```
|
||||
|
||||
_See code: [dist/commands/whoami.js](https://github.com/kamilkisiela/graphql-hive/blob/v0.19.5/dist/commands/whoami.js)_
|
||||
_See code:
|
||||
[dist/commands/whoami.js](https://github.com/kamilkisiela/graphql-hive/blob/v0.19.5/dist/commands/whoami.js)_
|
||||
|
||||
<!-- commandsstop -->
|
||||
|
||||
|
|
@ -302,7 +313,8 @@ _See code: [dist/commands/whoami.js](https://github.com/kamilkisiela/graphql-hiv
|
|||
|
||||
# Config
|
||||
|
||||
In addition to using the CLI args, you can also define your configuration in a JSON file which the CLI would pick up when it runs.
|
||||
In addition to using the CLI args, you can also define your configuration in a JSON file which the
|
||||
CLI would pick up when it runs.
|
||||
|
||||
You can use the `HIVE_CONFIG` environment variable to define the path to the JSON file as follows:
|
||||
|
||||
|
|
|
|||
|
|
@ -1,22 +1,22 @@
|
|||
{
|
||||
"name": "@graphql-hive/cli",
|
||||
"description": "A CLI util to manage and control your GraphQL Hive",
|
||||
"version": "0.19.10",
|
||||
"author": {
|
||||
"email": "contact@the-guild.dev",
|
||||
"name": "The Guild",
|
||||
"url": "https://the-guild.dev"
|
||||
},
|
||||
"description": "A CLI util to manage and control your GraphQL Hive",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "kamilkisiela/graphql-hive",
|
||||
"directory": "packages/libraries/cli"
|
||||
},
|
||||
"homepage": "https://graphql-hive.com",
|
||||
"author": {
|
||||
"email": "contact@the-guild.dev",
|
||||
"name": "The Guild",
|
||||
"url": "https://the-guild.dev"
|
||||
},
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"graphql"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"hive": "./bin/run"
|
||||
},
|
||||
|
|
@ -26,19 +26,22 @@
|
|||
"/dist",
|
||||
"/oclif.manifest.json"
|
||||
],
|
||||
"keywords": [
|
||||
"graphql"
|
||||
],
|
||||
"scripts": {
|
||||
"start": "./bin/dev",
|
||||
"prebuild": "rimraf dist",
|
||||
"build": "tsc",
|
||||
"postpack": "rm -f oclif.manifest.json",
|
||||
"prepack": "rimraf lib && tsc -b && oclif manifest && oclif readme",
|
||||
"oclif:pack": "npm pack && pnpm oclif pack tarballs --no-xz",
|
||||
"oclif:upload": "pnpm oclif upload tarballs --no-xz",
|
||||
"version": "oclif readme && git add README.md",
|
||||
"postpack": "rm -f oclif.manifest.json",
|
||||
"prebuild": "rimraf dist",
|
||||
"prepack": "rimraf lib && tsc -b && oclif manifest && oclif readme",
|
||||
"schema:check:federation": "pnpm start schema:check examples/federation.graphql --service reviews",
|
||||
"schema:check:single": "pnpm start schema:check examples/single.graphql",
|
||||
"schema:check:stitching": "pnpm start schema:check examples/stitching.graphql --service posts",
|
||||
"schema:check:federation": "pnpm start schema:check examples/federation.graphql --service reviews",
|
||||
"schema:publish:federation": "pnpm start schema:publish --service reviews examples/federation.reviews.graphql"
|
||||
"schema:publish:federation": "pnpm start schema:publish --service reviews examples/federation.reviews.graphql",
|
||||
"start": "./bin/dev",
|
||||
"version": "oclif readme && git add README.md"
|
||||
},
|
||||
"dependencies": {
|
||||
"@graphql-hive/core": "0.2.3",
|
||||
|
|
@ -46,8 +49,8 @@
|
|||
"@graphql-tools/code-file-loader": "~7.3.6",
|
||||
"@graphql-tools/graphql-file-loader": "~7.5.5",
|
||||
"@graphql-tools/json-file-loader": "~7.4.6",
|
||||
"@graphql-tools/url-loader": "~7.16.4",
|
||||
"@graphql-tools/load": "~7.7.7",
|
||||
"@graphql-tools/url-loader": "~7.16.4",
|
||||
"@graphql-tools/utils": "8.12.0",
|
||||
"@oclif/core": "^1.20.4",
|
||||
"@oclif/plugin-help": "5.1.19",
|
||||
|
|
@ -61,14 +64,18 @@
|
|||
"log-symbols": "4.1.0",
|
||||
"mkdirp": "1.0.4",
|
||||
"rimraf": "3.0.2",
|
||||
"tslib": "2.4.1",
|
||||
"ts-node": "10.9.1"
|
||||
"ts-node": "10.9.1",
|
||||
"tslib": "2.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"oclif": "^3.2.25",
|
||||
"@types/env-ci": "3.1.1",
|
||||
"@types/git-parse": "2.1.2",
|
||||
"@types/mkdirp": "1.0.2"
|
||||
"@types/mkdirp": "1.0.2",
|
||||
"oclif": "^3.2.25"
|
||||
},
|
||||
"publishConfig": {
|
||||
"registry": "https://registry.npmjs.org",
|
||||
"access": "public"
|
||||
},
|
||||
"oclif": {
|
||||
"commands": "./dist/commands",
|
||||
|
|
@ -82,12 +89,5 @@
|
|||
"bucket": "graphql-hive-cli"
|
||||
}
|
||||
}
|
||||
},
|
||||
"publishConfig": {
|
||||
"registry": "https://registry.npmjs.org",
|
||||
"access": "public"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ export default abstract class extends Command {
|
|||
TArgs extends {
|
||||
[key: string]: any;
|
||||
},
|
||||
TKey extends keyof TArgs
|
||||
TKey extends keyof TArgs,
|
||||
>({
|
||||
key,
|
||||
args,
|
||||
|
|
@ -104,7 +104,7 @@ export default abstract class extends Command {
|
|||
TArgs extends {
|
||||
[key: string]: any;
|
||||
},
|
||||
TKey extends keyof TArgs
|
||||
TKey extends keyof TArgs,
|
||||
>(key: TKey, args: TArgs): TArgs[TKey] | undefined {
|
||||
if (args[key]) {
|
||||
return args[key];
|
||||
|
|
@ -129,7 +129,7 @@ export default abstract class extends Command {
|
|||
'graphql-client-name': 'Hive CLI',
|
||||
'graphql-client-version': this.config.version,
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -163,10 +163,12 @@ export default abstract class extends Command {
|
|||
TFlags extends {
|
||||
require: string[];
|
||||
[key: string]: any;
|
||||
}
|
||||
},
|
||||
>(flags: TFlags) {
|
||||
if (flags.require && flags.require.length > 0) {
|
||||
await Promise.all(flags.require.map(mod => import(require.resolve(mod, { paths: [process.cwd()] }))));
|
||||
await Promise.all(
|
||||
flags.require.map(mod => import(require.resolve(mod, { paths: [process.cwd()] }))),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ export default class OperationsCheck extends Command {
|
|||
|
||||
const invalidOperations = validate(
|
||||
schema,
|
||||
operations.map(s => new Source(s.content, s.location))
|
||||
operations.map(s => new Source(s.content, s.location)),
|
||||
);
|
||||
|
||||
if (invalidOperations.length === 0) {
|
||||
|
|
@ -85,7 +85,9 @@ export default class OperationsCheck extends Command {
|
|||
|
||||
this.fail('Some operations are invalid');
|
||||
|
||||
this.log(['', `Total: ${operations.length}`, `Invalid: ${invalidOperations.length}`, ''].join('\n'));
|
||||
this.log(
|
||||
['', `Total: ${operations.length}`, `Invalid: ${invalidOperations.length}`, ''].join('\n'),
|
||||
);
|
||||
|
||||
this.printInvalidDocuments(invalidOperations, 'errors');
|
||||
} catch (error) {
|
||||
|
|
@ -98,7 +100,10 @@ export default class OperationsCheck extends Command {
|
|||
}
|
||||
}
|
||||
|
||||
private printInvalidDocuments(invalidDocuments: InvalidDocument[], listKey: 'errors' | 'deprecated'): void {
|
||||
private printInvalidDocuments(
|
||||
invalidDocuments: InvalidDocument[],
|
||||
listKey: 'errors' | 'deprecated',
|
||||
): void {
|
||||
invalidDocuments.forEach(doc => {
|
||||
if (doc.errors.length) {
|
||||
this.renderErrors(doc.source.name, doc[listKey]).forEach(line => {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@ export default class OperationsPublish extends Command {
|
|||
description: 'api token',
|
||||
}),
|
||||
require: Flags.string({
|
||||
description: 'Loads specific require.extensions before running the codegen and reading the configuration',
|
||||
description:
|
||||
'Loads specific require.extensions before running the codegen and reading the configuration',
|
||||
default: [],
|
||||
multiple: true,
|
||||
}),
|
||||
|
|
@ -54,9 +55,11 @@ export default class OperationsPublish extends Command {
|
|||
const noMissingHashes = operations.some(op => !!op.operationHash);
|
||||
|
||||
if (noMissingHashes) {
|
||||
const comparisonResult = await this.registryApi(registry, token).comparePersistedOperations({
|
||||
hashes: operations.map(op => op.operationHash!),
|
||||
});
|
||||
const comparisonResult = await this.registryApi(registry, token).comparePersistedOperations(
|
||||
{
|
||||
hashes: operations.map(op => op.operationHash!),
|
||||
},
|
||||
);
|
||||
|
||||
const operationsToPublish = comparisonResult.comparePersistedOperations;
|
||||
|
||||
|
|
@ -67,9 +70,13 @@ export default class OperationsPublish extends Command {
|
|||
|
||||
if (!operations.length) {
|
||||
return this.success(
|
||||
[`Nothing to publish`, '', ` Total: ${collectedOperationsTotal}`, ` Unchanged: ${unchangedTotal}`, ''].join(
|
||||
'\n'
|
||||
)
|
||||
[
|
||||
`Nothing to publish`,
|
||||
'',
|
||||
` Total: ${collectedOperationsTotal}`,
|
||||
` Unchanged: ${unchangedTotal}`,
|
||||
'',
|
||||
].join('\n'),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -86,7 +93,7 @@ export default class OperationsPublish extends Command {
|
|||
` Total: ${summary.total}`,
|
||||
` Unchanged: ${summary.unchanged}`,
|
||||
'',
|
||||
].join('\n')
|
||||
].join('\n'),
|
||||
);
|
||||
} else {
|
||||
this.error('OOPS! An error occurred in publishing the operation(s)');
|
||||
|
|
|
|||
|
|
@ -25,7 +25,8 @@ export default class SchemaCheck extends Command {
|
|||
default: false,
|
||||
}),
|
||||
require: Flags.string({
|
||||
description: 'Loads specific require.extensions before running the codegen and reading the configuration',
|
||||
description:
|
||||
'Loads specific require.extensions before running the codegen and reading the configuration',
|
||||
default: [],
|
||||
multiple: true,
|
||||
}),
|
||||
|
|
@ -70,7 +71,10 @@ export default class SchemaCheck extends Command {
|
|||
invariant(typeof sdl === 'string' && sdl.length > 0, 'Schema seems empty');
|
||||
|
||||
if (usesGitHubApp) {
|
||||
invariant(typeof commit === 'string', `Couldn't resolve commit sha required for GitHub Application`);
|
||||
invariant(
|
||||
typeof commit === 'string',
|
||||
`Couldn't resolve commit sha required for GitHub Application`,
|
||||
);
|
||||
}
|
||||
|
||||
const result = await this.registryApi(registry, token).schemaCheck({
|
||||
|
|
|
|||
|
|
@ -42,10 +42,12 @@ export default class SchemaPublish extends Command {
|
|||
default: false,
|
||||
}),
|
||||
experimental_acceptBreakingChanges: Flags.boolean({
|
||||
description: '(experimental) accept breaking changes and mark schema as valid (only if composable)',
|
||||
description:
|
||||
'(experimental) accept breaking changes and mark schema as valid (only if composable)',
|
||||
}),
|
||||
require: Flags.string({
|
||||
description: 'Loads specific require.extensions before running the codegen and reading the configuration',
|
||||
description:
|
||||
'Loads specific require.extensions before running the codegen and reading the configuration',
|
||||
default: [],
|
||||
multiple: true,
|
||||
}),
|
||||
|
|
@ -76,7 +78,7 @@ export default class SchemaPublish extends Command {
|
|||
|
||||
if (!exists) {
|
||||
throw new Error(
|
||||
`Failed to load metadata from "${metadata}": Please specify a path to an existing file, or a string with valid JSON.`
|
||||
`Failed to load metadata from "${metadata}": Please specify a path to an existing file, or a string with valid JSON.`,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -87,7 +89,7 @@ export default class SchemaPublish extends Command {
|
|||
return fileContent;
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`Failed to load metadata from file "${metadata}": Please make sure the file is readable and contains a valid JSON`
|
||||
`Failed to load metadata from file "${metadata}": Please make sure the file is readable and contains a valid JSON`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -114,7 +116,10 @@ export default class SchemaPublish extends Command {
|
|||
env: 'HIVE_TOKEN',
|
||||
});
|
||||
const force = this.maybe('force', flags);
|
||||
const experimental_acceptBreakingChanges = this.maybe('experimental_acceptBreakingChanges', flags);
|
||||
const experimental_acceptBreakingChanges = this.maybe(
|
||||
'experimental_acceptBreakingChanges',
|
||||
flags,
|
||||
);
|
||||
const metadata = this.resolveMetadata(this.maybe('metadata', flags));
|
||||
const usesGitHubApp = this.maybe('github', flags) === true;
|
||||
|
||||
|
|
@ -152,7 +157,9 @@ export default class SchemaPublish extends Command {
|
|||
} catch (err) {
|
||||
if (err instanceof GraphQLError) {
|
||||
const location = err.locations?.[0];
|
||||
const locationString = location ? ` at line ${location.line}, column ${location.column}` : '';
|
||||
const locationString = location
|
||||
? ` at line ${location.line}, column ${location.column}`
|
||||
: '';
|
||||
throw new Error(`The SDL is not valid${locationString}:\n ${err.message}`);
|
||||
}
|
||||
throw err;
|
||||
|
|
@ -191,10 +198,14 @@ export default class SchemaPublish extends Command {
|
|||
this.info(`Available at ${result.schemaPublish.linkToWebsite}`);
|
||||
}
|
||||
} else if (result.schemaPublish.__typename === 'SchemaPublishMissingServiceError') {
|
||||
this.fail(`${result.schemaPublish.missingServiceError} Please use the '--service <name>' parameter.`);
|
||||
this.fail(
|
||||
`${result.schemaPublish.missingServiceError} Please use the '--service <name>' parameter.`,
|
||||
);
|
||||
this.exit(1);
|
||||
} else if (result.schemaPublish.__typename === 'SchemaPublishMissingUrlError') {
|
||||
this.fail(`${result.schemaPublish.missingUrlError} Please use the '--url <url>' parameter.`);
|
||||
this.fail(
|
||||
`${result.schemaPublish.missingUrlError} Please use the '--url <url>' parameter.`,
|
||||
);
|
||||
this.exit(1);
|
||||
} else if (result.schemaPublish.__typename === 'SchemaPublishError') {
|
||||
const changes = result.schemaPublish.changes;
|
||||
|
|
@ -220,7 +231,9 @@ export default class SchemaPublish extends Command {
|
|||
} else if (result.schemaPublish.__typename === 'GitHubSchemaPublishSuccess') {
|
||||
this.success(result.schemaPublish.message);
|
||||
} else {
|
||||
this.error('message' in result.schemaPublish ? result.schemaPublish.message : 'Unknown error');
|
||||
this.error(
|
||||
'message' in result.schemaPublish ? result.schemaPublish.message : 'Unknown error',
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof Errors.ExitError) {
|
||||
|
|
|
|||
|
|
@ -42,7 +42,9 @@ function useGitHubAction(): CIRunner {
|
|||
env() {
|
||||
const isPr =
|
||||
// eslint-disable-next-line no-process-env
|
||||
process.env.GITHUB_EVENT_NAME === 'pull_request' || process.env.GITHUB_EVENT_NAME === 'pull_request_target';
|
||||
process.env.GITHUB_EVENT_NAME === 'pull_request' ||
|
||||
// eslint-disable-next-line no-process-env
|
||||
process.env.GITHUB_EVENT_NAME === 'pull_request_target';
|
||||
|
||||
if (isPr) {
|
||||
try {
|
||||
|
|
@ -85,7 +87,8 @@ export async function gitInfo(noGit: () => void) {
|
|||
|
||||
if (!commit || !author) {
|
||||
const rootFromEnv = 'root' in env ? env.root : null;
|
||||
const git = rootFromEnv ?? findParentDir(__dirname, '.git') ?? findParentDir(process.cwd(), '.git');
|
||||
const git =
|
||||
rootFromEnv ?? findParentDir(__dirname, '.git') ?? findParentDir(process.cwd(), '.git');
|
||||
|
||||
if (git) {
|
||||
const commits = await gitToJs(git);
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ export async function loadOperations(
|
|||
file: string,
|
||||
options?: {
|
||||
normalize?: boolean;
|
||||
}
|
||||
},
|
||||
): Promise<
|
||||
Array<{
|
||||
operationHash?: string;
|
||||
|
|
@ -24,7 +24,7 @@ export async function loadOperations(
|
|||
const output: Record<string, string> = JSON.parse(
|
||||
await fs.readFile(file, {
|
||||
encoding: 'utf-8',
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
const operations: Array<{
|
||||
|
|
|
|||
|
|
@ -4,64 +4,90 @@
|
|||
|
||||
### Patch Changes
|
||||
|
||||
- [#668](https://github.com/kamilkisiela/graphql-hive/pull/668) [`e116841`](https://github.com/kamilkisiela/graphql-hive/commit/e116841a739bfd7f37c4a826544301cf23d61637) Thanks [@kamilkisiela](https://github.com/kamilkisiela)! - Fix ESM/CJS issue
|
||||
- [#668](https://github.com/kamilkisiela/graphql-hive/pull/668)
|
||||
[`e116841`](https://github.com/kamilkisiela/graphql-hive/commit/e116841a739bfd7f37c4a826544301cf23d61637)
|
||||
Thanks [@kamilkisiela](https://github.com/kamilkisiela)! - Fix ESM/CJS issue
|
||||
|
||||
- Updated dependencies [[`e116841`](https://github.com/kamilkisiela/graphql-hive/commit/e116841a739bfd7f37c4a826544301cf23d61637)]:
|
||||
- Updated dependencies
|
||||
[[`e116841`](https://github.com/kamilkisiela/graphql-hive/commit/e116841a739bfd7f37c4a826544301cf23d61637)]:
|
||||
- @graphql-hive/core@0.2.3
|
||||
|
||||
## 0.21.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#655](https://github.com/kamilkisiela/graphql-hive/pull/655) [`2cbf27f`](https://github.com/kamilkisiela/graphql-hive/commit/2cbf27fdc9c18749b8969adb6d1598338762dba2) Thanks [@n1ru4l](https://github.com/n1ru4l)! - Add User-Agent header to all http requests
|
||||
- [#655](https://github.com/kamilkisiela/graphql-hive/pull/655)
|
||||
[`2cbf27f`](https://github.com/kamilkisiela/graphql-hive/commit/2cbf27fdc9c18749b8969adb6d1598338762dba2)
|
||||
Thanks [@n1ru4l](https://github.com/n1ru4l)! - Add User-Agent header to all http requests
|
||||
|
||||
## 0.21.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#466](https://github.com/kamilkisiela/graphql-hive/pull/466) [`2e036ac`](https://github.com/kamilkisiela/graphql-hive/commit/2e036acc4ce1c27a493e90481bb10f5886c0a00c) Thanks [@ardatan](https://github.com/ardatan)! - Update GraphQL Tools packages
|
||||
- [#466](https://github.com/kamilkisiela/graphql-hive/pull/466)
|
||||
[`2e036ac`](https://github.com/kamilkisiela/graphql-hive/commit/2e036acc4ce1c27a493e90481bb10f5886c0a00c)
|
||||
Thanks [@ardatan](https://github.com/ardatan)! - Update GraphQL Tools packages
|
||||
|
||||
## 0.21.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#563](https://github.com/kamilkisiela/graphql-hive/pull/563) [`d58a470`](https://github.com/kamilkisiela/graphql-hive/commit/d58a470916b213230f495e896fe99ec0baa225e2) Thanks [@PabloSzx](https://github.com/PabloSzx)! - Fix createServicesFetcher handling null service url
|
||||
- [#563](https://github.com/kamilkisiela/graphql-hive/pull/563)
|
||||
[`d58a470`](https://github.com/kamilkisiela/graphql-hive/commit/d58a470916b213230f495e896fe99ec0baa225e2)
|
||||
Thanks [@PabloSzx](https://github.com/PabloSzx)! - Fix createServicesFetcher handling null service
|
||||
url
|
||||
|
||||
## 0.20.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#456](https://github.com/kamilkisiela/graphql-hive/pull/456) [`fb9b624`](https://github.com/kamilkisiela/graphql-hive/commit/fb9b624ab80ff39658c9ecd45b55d10d906e15e7) Thanks [@dimatill](https://github.com/dimatill)! - (processVariables: true) do not collect input when corresponding variable is missing
|
||||
- [#456](https://github.com/kamilkisiela/graphql-hive/pull/456)
|
||||
[`fb9b624`](https://github.com/kamilkisiela/graphql-hive/commit/fb9b624ab80ff39658c9ecd45b55d10d906e15e7)
|
||||
Thanks [@dimatill](https://github.com/dimatill)! - (processVariables: true) do not collect input
|
||||
when corresponding variable is missing
|
||||
|
||||
## 0.20.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#499](https://github.com/kamilkisiela/graphql-hive/pull/499) [`682cde8`](https://github.com/kamilkisiela/graphql-hive/commit/682cde81092fcb3a55de7f24035be4f2f64abfb3) Thanks [@kamilkisiela](https://github.com/kamilkisiela)! - Add Self-Hosting options
|
||||
- [#499](https://github.com/kamilkisiela/graphql-hive/pull/499)
|
||||
[`682cde8`](https://github.com/kamilkisiela/graphql-hive/commit/682cde81092fcb3a55de7f24035be4f2f64abfb3)
|
||||
Thanks [@kamilkisiela](https://github.com/kamilkisiela)! - Add Self-Hosting options
|
||||
|
||||
## 0.19.0
|
||||
|
||||
### Major Changes
|
||||
|
||||
- [#435](https://github.com/kamilkisiela/graphql-hive/pull/435) [`a79c253`](https://github.com/kamilkisiela/graphql-hive/commit/a79c253253614e44d01fef411016d353ef8c255e) Thanks [@kamilkisiela](https://github.com/kamilkisiela)! - Use ETag and If-None-Match to save bandwidth and improve performance
|
||||
- [#435](https://github.com/kamilkisiela/graphql-hive/pull/435)
|
||||
[`a79c253`](https://github.com/kamilkisiela/graphql-hive/commit/a79c253253614e44d01fef411016d353ef8c255e)
|
||||
Thanks [@kamilkisiela](https://github.com/kamilkisiela)! - Use ETag and If-None-Match to save
|
||||
bandwidth and improve performance
|
||||
|
||||
## 0.18.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#399](https://github.com/kamilkisiela/graphql-hive/pull/399) [`bd6e500`](https://github.com/kamilkisiela/graphql-hive/commit/bd6e500532ed4878a069883fadeaf3bb00e38aeb) Thanks [@kamilkisiela](https://github.com/kamilkisiela)! - Fix the wrong cacheKey from #397
|
||||
- [#399](https://github.com/kamilkisiela/graphql-hive/pull/399)
|
||||
[`bd6e500`](https://github.com/kamilkisiela/graphql-hive/commit/bd6e500532ed4878a069883fadeaf3bb00e38aeb)
|
||||
Thanks [@kamilkisiela](https://github.com/kamilkisiela)! - Fix the wrong cacheKey from #397
|
||||
|
||||
## 0.18.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#379](https://github.com/kamilkisiela/graphql-hive/pull/379) [`2e7c8f3`](https://github.com/kamilkisiela/graphql-hive/commit/2e7c8f3c94013f890f42ca1054287841478ba7a6) Thanks [@dimatill](https://github.com/dimatill)! - Collect input fields from variables (opt-in with `processVariables` flag)
|
||||
- [#379](https://github.com/kamilkisiela/graphql-hive/pull/379)
|
||||
[`2e7c8f3`](https://github.com/kamilkisiela/graphql-hive/commit/2e7c8f3c94013f890f42ca1054287841478ba7a6)
|
||||
Thanks [@dimatill](https://github.com/dimatill)! - Collect input fields from variables (opt-in
|
||||
with `processVariables` flag)
|
||||
|
||||
## 0.18.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#308](https://github.com/kamilkisiela/graphql-hive/pull/308) [`5a212f6`](https://github.com/kamilkisiela/graphql-hive/commit/5a212f61f206d7b73f8abf04667480851aa6066e) Thanks [@kamilkisiela](https://github.com/kamilkisiela)! - Avoid marking the same type as used twice
|
||||
- [#308](https://github.com/kamilkisiela/graphql-hive/pull/308)
|
||||
[`5a212f6`](https://github.com/kamilkisiela/graphql-hive/commit/5a212f61f206d7b73f8abf04667480851aa6066e)
|
||||
Thanks [@kamilkisiela](https://github.com/kamilkisiela)! - Avoid marking the same type as used
|
||||
twice
|
||||
|
||||
## 0.18.2
|
||||
|
||||
|
|
@ -348,7 +374,8 @@
|
|||
|
||||
### Minor Changes
|
||||
|
||||
- 30da7e7: When disabled, run everything in dry mode (only http agent is disabled). This should help to catch errors in development.
|
||||
- 30da7e7: When disabled, run everything in dry mode (only http agent is disabled). This should help
|
||||
to catch errors in development.
|
||||
|
||||
### Patch Changes
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
# GraphQL Hive Client
|
||||
|
||||
[GraphQL Hive](https://graphql-hive.com) is a GraphQL schemas registry where you can host, manage and collaborate on all your GraphQL schemas and operations, compatible with all architecture: schema stitching, federation, or just a good old monolith.
|
||||
[GraphQL Hive](https://graphql-hive.com) is a GraphQL schemas registry where you can host, manage
|
||||
and collaborate on all your GraphQL schemas and operations, compatible with all architecture: schema
|
||||
stitching, federation, or just a good old monolith.
|
||||
|
||||
GraphQL Hive is currently available as a hosted service to be used by all.
|
||||
We take care of the heavy lifting behind the scenes be managing the registry, scaling it for your needs, to free your time to focus on the most important things at hand.
|
||||
GraphQL Hive is currently available as a hosted service to be used by all. We take care of the heavy
|
||||
lifting behind the scenes be managing the registry, scaling it for your needs, to free your time to
|
||||
focus on the most important things at hand.
|
||||
|
||||
## Installation
|
||||
|
||||
|
|
@ -13,11 +16,13 @@ npm install @graphql-hive/client
|
|||
|
||||
## Basic Usage
|
||||
|
||||
Hive Client comes with generic client and plugins for [Envelop](https://envelop.dev) and [Apollo Server](https://github.com/apollographql/apollo-server)
|
||||
Hive Client comes with generic client and plugins for [Envelop](https://envelop.dev) and
|
||||
[Apollo Server](https://github.com/apollographql/apollo-server)
|
||||
|
||||
### With GraphQL Yoga
|
||||
|
||||
[GraphQL Yoga](https://www.the-guild.dev/graphql/yoga-server) is a cross-platform GraphQL sever built on top of the envelop engine.
|
||||
[GraphQL Yoga](https://www.the-guild.dev/graphql/yoga-server) is a cross-platform GraphQL sever
|
||||
built on top of the envelop engine.
|
||||
|
||||
```ts
|
||||
import { createYoga } from '@graphql-yoga/node'
|
||||
|
|
@ -45,7 +50,9 @@ server.start()
|
|||
|
||||
### With Envelop
|
||||
|
||||
If you're not familiar with Envelop - in "short" it's a lightweight JavaScript library for wrapping GraphQL execution layer and flow, allowing developers to develop, share and collaborate on GraphQL-related plugins, while filling the missing pieces in GraphQL implementations.
|
||||
If you're not familiar with Envelop - in "short" it's a lightweight JavaScript library for wrapping
|
||||
GraphQL execution layer and flow, allowing developers to develop, share and collaborate on
|
||||
GraphQL-related plugins, while filling the missing pieces in GraphQL implementations.
|
||||
|
||||
Here's [more](https://github.com/dotansimha/envelop#envelop) on that topic.
|
||||
|
||||
|
|
@ -102,10 +109,12 @@ const server = new ApolloServer({
|
|||
|
||||
First you need to instantiate the Hive Client.
|
||||
|
||||
The `collectUsage` method accepts the same arguments as execute function of graphql-js and returns a function that expects the execution result object.
|
||||
The `collectUsage` method accepts the same arguments as execute function of graphql-js and returns a
|
||||
function that expects the execution result object.
|
||||
|
||||
- `collectUsage(args)` - should be called when a GraphQL execution starts.
|
||||
- `finish(result)` (function returned by `collectUsage(args)`) - has to be invoked right after execution finishes.
|
||||
- `finish(result)` (function returned by `collectUsage(args)`) - has to be invoked right after
|
||||
execution finishes.
|
||||
|
||||
```ts
|
||||
import express from 'express'
|
||||
|
|
@ -145,14 +154,17 @@ app.post(
|
|||
|
||||
### Using the registry when Stitching
|
||||
|
||||
Stitching could be done in many ways, that's why `@graphql-hive/client` provide generic functions, not something dedicated for stitching. Unfortunately the implementation of gateway + polling is up to you.
|
||||
Stitching could be done in many ways, that's why `@graphql-hive/client` provide generic functions,
|
||||
not something dedicated for stitching. Unfortunately the implementation of gateway + polling is up
|
||||
to you.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- `HIVE_CDN_ENDPOINT` - the endpoint Hive generated for you in the previous step
|
||||
- `HIVE_CDN_KEY` - the access key
|
||||
|
||||
The `createServicesFetcher` factory function returns another function that is responsible for fetching a list of services from Hive's high-availability endpoint.
|
||||
The `createServicesFetcher` factory function returns another function that is responsible for
|
||||
fetching a list of services from Hive's high-availability endpoint.
|
||||
|
||||
```ts
|
||||
import { createServicesFetcher } from '@graphql-hive/client'
|
||||
|
|
@ -214,7 +226,9 @@ server.listen().then(({ url }) => {
|
|||
|
||||
### Client Info
|
||||
|
||||
The schema usage operation information can be enriched with meta information that will be displayed on the Hive dashboard in order to get a better understanding of the origin of an executed GraphQL operation.
|
||||
The schema usage operation information can be enriched with meta information that will be displayed
|
||||
on the Hive dashboard in order to get a better understanding of the origin of an executed GraphQL
|
||||
operation.
|
||||
|
||||
### GraphQL Yoga Example
|
||||
|
||||
|
|
@ -307,9 +321,11 @@ const server = new ApolloServer({
|
|||
|
||||
## Self-Hosting
|
||||
|
||||
To align the client with your own instance of GraphQL Hive, you should use `selfHosting` options in the client configuration.
|
||||
To align the client with your own instance of GraphQL Hive, you should use `selfHosting` options in
|
||||
the client configuration.
|
||||
|
||||
The example is based on GraphQL Yoga, but the same configuration applies to Apollo Server and others.
|
||||
The example is based on GraphQL Yoga, but the same configuration applies to Apollo Server and
|
||||
others.
|
||||
|
||||
```ts
|
||||
import { createYoga } from '@graphql-yoga/node'
|
||||
|
|
@ -332,4 +348,5 @@ const server = createYoga({
|
|||
server.start()
|
||||
```
|
||||
|
||||
> The `selfHosting` options take precedence over the deprecated `options.hosting.endpoint` and `options.usage.endpoint`.
|
||||
> The `selfHosting` options take precedence over the deprecated `options.hosting.endpoint` and
|
||||
> `options.usage.endpoint`.
|
||||
|
|
|
|||
|
|
@ -1,21 +1,20 @@
|
|||
{
|
||||
"name": "@graphql-hive/client",
|
||||
"description": "A NodeJS client for GraphQL Hive",
|
||||
"version": "0.21.3",
|
||||
"author": {
|
||||
"email": "contact@the-guild.dev",
|
||||
"name": "The Guild",
|
||||
"url": "https://the-guild.dev"
|
||||
},
|
||||
"type": "module",
|
||||
"description": "A NodeJS client for GraphQL Hive",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "kamilkisiela/graphql-hive",
|
||||
"directory": "packages/libraries/client"
|
||||
},
|
||||
"homepage": "https://graphql-hive.com",
|
||||
"author": {
|
||||
"email": "contact@the-guild.dev",
|
||||
"name": "The Guild",
|
||||
"url": "https://the-guild.dev"
|
||||
},
|
||||
"license": "MIT",
|
||||
"sideEffects": false,
|
||||
"type": "module",
|
||||
"main": "dist/cjs/index.js",
|
||||
"module": "dist/esm/index.js",
|
||||
"exports": {
|
||||
|
|
@ -36,9 +35,6 @@
|
|||
"./package.json": "./package.json"
|
||||
},
|
||||
"typings": "dist/typings/index.d.ts",
|
||||
"typescript": {
|
||||
"definition": "dist/typings/index.d.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "node scripts/update-version.mjs && bob build",
|
||||
"check:build": "bob check"
|
||||
|
|
@ -56,15 +52,19 @@
|
|||
"devDependencies": {
|
||||
"@apollo/federation": "0.38.1",
|
||||
"@envelop/types": "3.0.0",
|
||||
"graphql-yoga": "3.0.0-next.12",
|
||||
"@types/async-retry": "1.4.5",
|
||||
"apollo-server-core": "3.11.1",
|
||||
"apollo-server-plugin-base": "3.5.3",
|
||||
"graphql-yoga": "3.0.0-next.12",
|
||||
"nock": "13.2.4"
|
||||
},
|
||||
"publishConfig": {
|
||||
"registry": "https://registry.npmjs.org",
|
||||
"access": "public",
|
||||
"directory": "dist"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"typescript": {
|
||||
"definition": "dist/typings/index.d.ts"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
import type { ApolloServerPlugin } from 'apollo-server-plugin-base';
|
||||
import type { DocumentNode } from 'graphql';
|
||||
import type { HiveClient, HivePluginOptions, SupergraphSDLFetcherOptions } from './internal/types.js';
|
||||
import type {
|
||||
HiveClient,
|
||||
HivePluginOptions,
|
||||
SupergraphSDLFetcherOptions,
|
||||
} from './internal/types.js';
|
||||
import { createHash } from 'crypto';
|
||||
import axios from 'axios';
|
||||
import { createHive } from './client.js';
|
||||
|
|
@ -59,9 +63,14 @@ export function createSupergraphSDLFetcher({ endpoint, key }: SupergraphSDLFetch
|
|||
};
|
||||
}
|
||||
|
||||
export function createSupergraphManager(options: { pollIntervalInMs?: number } & SupergraphSDLFetcherOptions) {
|
||||
export function createSupergraphManager(
|
||||
options: { pollIntervalInMs?: number } & SupergraphSDLFetcherOptions,
|
||||
) {
|
||||
const pollIntervalInMs = options.pollIntervalInMs ?? 30_000;
|
||||
const fetchSupergraph = createSupergraphSDLFetcher({ endpoint: options.endpoint, key: options.key });
|
||||
const fetchSupergraph = createSupergraphSDLFetcher({
|
||||
endpoint: options.endpoint,
|
||||
key: options.key,
|
||||
});
|
||||
let timer: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
return {
|
||||
|
|
@ -79,7 +88,9 @@ export function createSupergraphManager(options: { pollIntervalInMs?: number } &
|
|||
hooks.update?.(result.supergraphSdl);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Failed to update supergraph: ${error instanceof Error ? error.message : error}`);
|
||||
console.error(
|
||||
`Failed to update supergraph: ${error instanceof Error ? error.message : error}`,
|
||||
);
|
||||
}
|
||||
poll();
|
||||
}, pollIntervalInMs);
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ export function createHive(options: HivePluginOptions): HiveClient {
|
|||
timeout: 30_000,
|
||||
decompress: true,
|
||||
responseType: 'json',
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (response.status >= 200 && response.status < 300) {
|
||||
|
|
@ -108,10 +108,24 @@ export function createHive(options: HivePluginOptions): HiveClient {
|
|||
if (result.data?.tokenInfo.__typename === 'TokenInfo') {
|
||||
const { tokenInfo } = result.data;
|
||||
|
||||
const { organization, project, target, canReportSchema, canCollectUsage, canReadOperations } = tokenInfo;
|
||||
const print = createPrinter([tokenInfo.token.name, organization.name, project.name, target.name]);
|
||||
const {
|
||||
organization,
|
||||
project,
|
||||
target,
|
||||
canReportSchema,
|
||||
canCollectUsage,
|
||||
canReadOperations,
|
||||
} = tokenInfo;
|
||||
const print = createPrinter([
|
||||
tokenInfo.token.name,
|
||||
organization.name,
|
||||
project.name,
|
||||
target.name,
|
||||
]);
|
||||
|
||||
const appUrl = options.selfHosting?.applicationUrl?.replace(/\/$/, '') ?? 'https://app.graphql-hive.com';
|
||||
const appUrl =
|
||||
options.selfHosting?.applicationUrl?.replace(/\/$/, '') ??
|
||||
'https://app.graphql-hive.com';
|
||||
const organizationUrl = `${appUrl}/${organization.cleanId}`;
|
||||
const projectUrl = `${organizationUrl}/${project.cleanId}`;
|
||||
const targetUrl = `${projectUrl}/${target.cleanId}`;
|
||||
|
|
@ -129,14 +143,18 @@ export function createHive(options: HivePluginOptions): HiveClient {
|
|||
`Can collect usage? ${print(canCollectUsage ? 'Yes' : 'No')}`,
|
||||
`Can read operations? ${print(canReadOperations ? 'Yes' : 'No')}`,
|
||||
'',
|
||||
].join('\n')
|
||||
].join('\n'),
|
||||
);
|
||||
} else if (result.data?.tokenInfo.message) {
|
||||
logger.error(`[hive][info] Token not found. Reason: ${result.data?.tokenInfo.message}`);
|
||||
logger.info(`[hive][info] How to create a token? https://docs.graphql-hive.com/features/tokens`);
|
||||
logger.info(
|
||||
`[hive][info] How to create a token? https://docs.graphql-hive.com/features/tokens`,
|
||||
);
|
||||
} else {
|
||||
logger.error(`[hive][info] ${result.errors![0].message}`);
|
||||
logger.info(`[hive][info] How to create a token? https://docs.graphql-hive.com/features/tokens`);
|
||||
logger.info(
|
||||
`[hive][info] How to create a token? https://docs.graphql-hive.com/features/tokens`,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
logger.error(`[hive][info] Error ${response.status}: ${response.statusText}`);
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ export function createServicesFetcher({ endpoint, key }: ServicesFetcherOptions)
|
|||
.update(service.name)
|
||||
.digest('base64'),
|
||||
...service,
|
||||
}))
|
||||
})),
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ export function createAgent<TEvent, TResult = void>(
|
|||
};
|
||||
body(): Buffer | string | Promise<string | Buffer>;
|
||||
headers?(): Record<string, string>;
|
||||
}
|
||||
},
|
||||
) {
|
||||
const options: Required<AgentOptions> = {
|
||||
timeout: 30_000,
|
||||
|
|
@ -117,9 +117,18 @@ export function createAgent<TEvent, TResult = void>(
|
|||
return send({ runOnce: true, throwOnError: true });
|
||||
}
|
||||
|
||||
async function send<T>(sendOptions: { runOnce?: boolean; throwOnError: true }): Promise<T | null | never>;
|
||||
async function send<T>(sendOptions: { runOnce?: boolean; throwOnError: false }): Promise<T | null>;
|
||||
async function send<T>(sendOptions?: { runOnce?: boolean; throwOnError: boolean }): Promise<T | null | never> {
|
||||
async function send<T>(sendOptions: {
|
||||
runOnce?: boolean;
|
||||
throwOnError: true;
|
||||
}): Promise<T | null | never>;
|
||||
async function send<T>(sendOptions: {
|
||||
runOnce?: boolean;
|
||||
throwOnError: false;
|
||||
}): Promise<T | null>;
|
||||
async function send<T>(sendOptions?: {
|
||||
runOnce?: boolean;
|
||||
throwOnError: boolean;
|
||||
}): Promise<T | null | never> {
|
||||
const runOnce = sendOptions?.runOnce ?? false;
|
||||
|
||||
if (!data.size()) {
|
||||
|
|
@ -180,7 +189,9 @@ export function createAgent<TEvent, TResult = void>(
|
|||
});
|
||||
|
||||
if (response.status < 200 || response.status >= 300) {
|
||||
throw new Error(`[hive][${prefix}] Failed to send data (HTTP status ${response.status}): ${response.data}`);
|
||||
throw new Error(
|
||||
`[hive][${prefix}] Failed to send data (HTTP status ${response.status}): ${response.data}`,
|
||||
);
|
||||
}
|
||||
|
||||
debugLog(`Sent!`);
|
||||
|
|
|
|||
|
|
@ -40,7 +40,9 @@ export function createOperationsStore(pluginOptions: HivePluginOptions): Operati
|
|||
|
||||
const load: OperationsStore['load'] = async () => {
|
||||
const response = await axios.post(
|
||||
selfHostingOptions?.graphqlEndpoint ?? operationsStoreOptions.endpoint ?? 'https://app.graphql-hive.com/graphql',
|
||||
selfHostingOptions?.graphqlEndpoint ??
|
||||
operationsStoreOptions.endpoint ??
|
||||
'https://app.graphql-hive.com/graphql',
|
||||
{
|
||||
query,
|
||||
operationName: 'loadStoredOperations',
|
||||
|
|
@ -51,7 +53,7 @@ export function createOperationsStore(pluginOptions: HivePluginOptions): Operati
|
|||
'content-type': 'application/json',
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const parsedData: {
|
||||
|
|
@ -70,7 +72,7 @@ export function createOperationsStore(pluginOptions: HivePluginOptions): Operati
|
|||
key,
|
||||
parse(document, {
|
||||
noLocation: true,
|
||||
})
|
||||
}),
|
||||
);
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -27,14 +27,18 @@ export function createReporting(pluginOptions: HivePluginOptions): SchemaReporte
|
|||
logIf(
|
||||
typeof reportingOptions.author !== 'string' || reportingOptions.author.length === 0,
|
||||
'[hive][reporting] author is missing',
|
||||
logger.error
|
||||
logger.error,
|
||||
);
|
||||
logIf(
|
||||
typeof reportingOptions.commit !== 'string' || reportingOptions.commit.length === 0,
|
||||
'[hive][reporting] commit is missing',
|
||||
logger.error
|
||||
logger.error,
|
||||
);
|
||||
logIf(
|
||||
typeof token !== 'string' || token.length === 0,
|
||||
'[hive][reporting] token is missing',
|
||||
logger.error,
|
||||
);
|
||||
logIf(typeof token !== 'string' || token.length === 0, '[hive][reporting] token is missing', logger.error);
|
||||
|
||||
let currentSchema: GraphQLSchema | null = null;
|
||||
const agent = createAgent<GraphQLSchema, ExecutionResult<SchemaPublishMutation>>(
|
||||
|
|
@ -42,7 +46,9 @@ export function createReporting(pluginOptions: HivePluginOptions): SchemaReporte
|
|||
logger,
|
||||
...(pluginOptions.agent ?? {}),
|
||||
endpoint:
|
||||
selfHostingOptions?.graphqlEndpoint ?? reportingOptions.endpoint ?? 'https://app.graphql-hive.com/graphql',
|
||||
selfHostingOptions?.graphqlEndpoint ??
|
||||
reportingOptions.endpoint ??
|
||||
'https://app.graphql-hive.com/graphql',
|
||||
token: token,
|
||||
enabled: pluginOptions.enabled,
|
||||
debug: pluginOptions.debug,
|
||||
|
|
@ -83,7 +89,7 @@ export function createReporting(pluginOptions: HivePluginOptions): SchemaReporte
|
|||
},
|
||||
});
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
|
|
@ -113,7 +119,9 @@ export function createReporting(pluginOptions: HivePluginOptions): SchemaReporte
|
|||
throw new Error('Service url is not defined');
|
||||
}
|
||||
case 'SchemaPublishError': {
|
||||
logger.info(`[hive][reporting] Published schema (forced with ${data.errors.total} errors)`);
|
||||
logger.info(
|
||||
`[hive][reporting] Published schema (forced with ${data.errors.total} errors)`,
|
||||
);
|
||||
data.errors.nodes.slice(0, 5).forEach(error => {
|
||||
logger.info(` - ${error.message}`);
|
||||
});
|
||||
|
|
@ -124,7 +132,7 @@ export function createReporting(pluginOptions: HivePluginOptions): SchemaReporte
|
|||
logger.error(
|
||||
`[hive][reporting] Failed to report schema: ${
|
||||
error instanceof Error && 'message' in error ? error.message : error
|
||||
}`
|
||||
}`,
|
||||
);
|
||||
}
|
||||
},
|
||||
|
|
@ -226,6 +234,8 @@ function printSchemaWithDirectives(schema: GraphQLSchema) {
|
|||
|
||||
async function printToSDL(schema: GraphQLSchema) {
|
||||
return stripIgnoredCharacters(
|
||||
isFederatedSchema(schema) ? await extractFederationServiceSDL(schema) : printSchemaWithDirectives(schema)
|
||||
isFederatedSchema(schema)
|
||||
? await extractFederationServiceSDL(schema)
|
||||
: printSchemaWithDirectives(schema),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,9 @@ export interface HiveClient {
|
|||
|
||||
export type AsyncIterableIteratorOrValue<T> = AsyncIterableIterator<T> | T;
|
||||
|
||||
export type CollectUsageCallback = (result: AsyncIterableIteratorOrValue<GraphQLErrorsResult>) => void;
|
||||
export type CollectUsageCallback = (
|
||||
result: AsyncIterableIteratorOrValue<GraphQLErrorsResult>,
|
||||
) => void;
|
||||
export interface ClientInfo {
|
||||
name: string;
|
||||
version: string;
|
||||
|
|
|
|||
|
|
@ -28,8 +28,20 @@ import { normalizeOperation } from '@graphql-hive/core';
|
|||
import { createAgent } from './agent.js';
|
||||
import { randomSampling } from './sampling.js';
|
||||
import { version } from '../version.js';
|
||||
import { cache, cacheDocumentKey, measureDuration, memo, isAsyncIterableIterator, logIf } from './utils.js';
|
||||
import type { HivePluginOptions, HiveUsagePluginOptions, CollectUsageCallback, ClientInfo } from './types.js';
|
||||
import {
|
||||
cache,
|
||||
cacheDocumentKey,
|
||||
measureDuration,
|
||||
memo,
|
||||
isAsyncIterableIterator,
|
||||
logIf,
|
||||
} from './utils.js';
|
||||
import type {
|
||||
HivePluginOptions,
|
||||
HiveUsagePluginOptions,
|
||||
CollectUsageCallback,
|
||||
ClientInfo,
|
||||
} from './types.js';
|
||||
|
||||
interface UsageCollector {
|
||||
collect(args: ExecutionArgs): CollectUsageCallback;
|
||||
|
|
@ -51,7 +63,8 @@ export function createUsage(pluginOptions: HivePluginOptions): UsageCollector {
|
|||
map: {},
|
||||
operations: [],
|
||||
};
|
||||
const options = typeof pluginOptions.usage === 'boolean' ? ({} as HiveUsagePluginOptions) : pluginOptions.usage;
|
||||
const options =
|
||||
typeof pluginOptions.usage === 'boolean' ? ({} as HiveUsagePluginOptions) : pluginOptions.usage;
|
||||
const selfHostingOptions = pluginOptions.selfHosting;
|
||||
const logger = pluginOptions.agent?.logger ?? console;
|
||||
const collector = memo(createCollector, arg => arg.schema);
|
||||
|
|
@ -62,7 +75,10 @@ export function createUsage(pluginOptions: HivePluginOptions): UsageCollector {
|
|||
...(pluginOptions.agent ?? {
|
||||
maxSize: 1500,
|
||||
}),
|
||||
endpoint: selfHostingOptions?.usageEndpoint ?? options.endpoint ?? 'https://app.graphql-hive.com/usage',
|
||||
endpoint:
|
||||
selfHostingOptions?.usageEndpoint ??
|
||||
options.endpoint ??
|
||||
'https://app.graphql-hive.com/usage',
|
||||
token: pluginOptions.token,
|
||||
enabled: pluginOptions.enabled,
|
||||
debug: pluginOptions.debug,
|
||||
|
|
@ -116,13 +132,13 @@ export function createUsage(pluginOptions: HivePluginOptions): UsageCollector {
|
|||
body() {
|
||||
return JSON.stringify(report);
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
logIf(
|
||||
typeof pluginOptions.token !== 'string' || pluginOptions.token.length === 0,
|
||||
'[hive][usage] token is missing',
|
||||
logger.error
|
||||
logger.error,
|
||||
);
|
||||
|
||||
const shouldInclude = randomSampling(options.sampleRate ?? 1.0);
|
||||
|
|
@ -141,7 +157,7 @@ export function createUsage(pluginOptions: HivePluginOptions): UsageCollector {
|
|||
}
|
||||
|
||||
const rootOperation = args.document.definitions.find(
|
||||
o => o.kind === Kind.OPERATION_DEFINITION
|
||||
o => o.kind === Kind.OPERATION_DEFINITION,
|
||||
) as OperationDefinitionNode;
|
||||
const document = args.document;
|
||||
const operationName = args.operationName || rootOperation.name?.value || 'anonymous';
|
||||
|
|
@ -175,7 +191,8 @@ export function createUsage(pluginOptions: HivePluginOptions): UsageCollector {
|
|||
},
|
||||
// TODO: operationHash is ready to accept hashes of persisted operations
|
||||
client:
|
||||
typeof args.contextValue !== 'undefined' && typeof options.clientInfo !== 'undefined'
|
||||
typeof args.contextValue !== 'undefined' &&
|
||||
typeof options.clientInfo !== 'undefined'
|
||||
? options.clientInfo(args.contextValue)
|
||||
: null,
|
||||
});
|
||||
|
|
@ -210,7 +227,7 @@ export function createCollector({
|
|||
doc: DocumentNode,
|
||||
variables: {
|
||||
[key: string]: unknown;
|
||||
} | null
|
||||
} | null,
|
||||
): CacheResult {
|
||||
const entries = new Set<string>();
|
||||
const collected_entire_named_types = new Set<string>();
|
||||
|
|
@ -377,7 +394,7 @@ export function createCollector({
|
|||
collectNode(node);
|
||||
collectInputType(parentInputTypeName, node.name.value);
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
for (const inputTypeName in collectedInputTypes) {
|
||||
|
|
@ -407,7 +424,7 @@ export function createCollector({
|
|||
function cacheKey(doc, variables) {
|
||||
return cacheDocumentKey(doc, processVariables === true ? variables : null);
|
||||
},
|
||||
LRU<CacheResult>(max, ttl)
|
||||
LRU<CacheResult>(max, ttl),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -426,7 +443,10 @@ function unwrapType(type: GraphQLType): GraphQLNamedType {
|
|||
return type;
|
||||
}
|
||||
|
||||
type GraphQLNamedInputType = Exclude<GraphQLNamedType, GraphQLObjectType | GraphQLInterfaceType | GraphQLUnionType>;
|
||||
type GraphQLNamedInputType = Exclude<
|
||||
GraphQLNamedType,
|
||||
GraphQLObjectType | GraphQLInterfaceType | GraphQLUnionType
|
||||
>;
|
||||
type GraphQLNamedOutputType = Exclude<GraphQLNamedType, GraphQLInputObjectType>;
|
||||
|
||||
export interface Report {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import { createHash } from 'crypto';
|
||||
import type { HiveClient, HivePluginOptions, AsyncIterableIteratorOrValue } from './types.js';
|
||||
|
||||
export function isAsyncIterableIterator<T>(value: AsyncIterableIteratorOrValue<T>): value is AsyncIterableIterator<T> {
|
||||
export function isAsyncIterableIterator<T>(
|
||||
value: AsyncIterableIteratorOrValue<T>,
|
||||
): value is AsyncIterableIterator<T> {
|
||||
return typeof (value as any)?.[Symbol.asyncIterator] === 'function';
|
||||
}
|
||||
|
||||
|
|
@ -29,7 +31,7 @@ export function cache<R, A, K, V>(
|
|||
has(key: K): boolean;
|
||||
set(key: K, value: R): void;
|
||||
get(key: K): R | undefined;
|
||||
}
|
||||
},
|
||||
) {
|
||||
return (arg: A, arg2: V) => {
|
||||
const key = cacheKeyFn(arg, arg2);
|
||||
|
|
@ -68,7 +70,7 @@ export function cacheDocumentKey<T, V>(doc: T, variables: V | null) {
|
|||
}
|
||||
|
||||
return '';
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -102,11 +104,15 @@ export function addProperty<T, K extends string>(key: K, value: undefined | null
|
|||
export function addProperty<T, K extends string, V>(
|
||||
key: K,
|
||||
value: V,
|
||||
obj: T
|
||||
obj: T,
|
||||
): T & {
|
||||
[k in K]: V;
|
||||
};
|
||||
export function addProperty<T, K extends string, V>(key: K, value: V | undefined | null, obj: T): any {
|
||||
export function addProperty<T, K extends string, V>(
|
||||
key: K,
|
||||
value: V | undefined | null,
|
||||
obj: T,
|
||||
): any {
|
||||
if (value === null || typeof value === 'undefined') {
|
||||
return obj;
|
||||
}
|
||||
|
|
@ -117,7 +123,9 @@ export function addProperty<T, K extends string, V>(key: K, value: V | undefined
|
|||
};
|
||||
}
|
||||
|
||||
export function isHiveClient(clientOrOptions: HiveClient | HivePluginOptions): clientOrOptions is HiveClient {
|
||||
export function isHiveClient(
|
||||
clientOrOptions: HiveClient | HivePluginOptions,
|
||||
): clientOrOptions is HiveClient {
|
||||
return 'operationsStore' in clientOrOptions;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -88,13 +88,15 @@ test('should use selfHosting.graphqlEndpoint if provided', async () => {
|
|||
expect(logger.info).toHaveBeenCalledWith(expect.stringContaining(`[hive][info] Token details`));
|
||||
expect(logger.info).toHaveBeenCalledWith(expect.stringMatching(/Token name: \s+ My Token/));
|
||||
expect(logger.info).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/Organization: \s+ Org \s+ http:\/\/localhost\/org-id/)
|
||||
expect.stringMatching(/Organization: \s+ Org \s+ http:\/\/localhost\/org-id/),
|
||||
);
|
||||
expect(logger.info).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/Project: \s+ Project \s+ http:\/\/localhost\/org-id\/project-id/)
|
||||
expect.stringMatching(/Project: \s+ Project \s+ http:\/\/localhost\/org-id\/project-id/),
|
||||
);
|
||||
expect(logger.info).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/Target: \s+ Target \s+ http:\/\/localhost\/org-id\/project-id\/target-id/)
|
||||
expect.stringMatching(
|
||||
/Target: \s+ Target \s+ http:\/\/localhost\/org-id\/project-id\/target-id/,
|
||||
),
|
||||
);
|
||||
expect(logger.info).toHaveBeenCalledWith(expect.stringMatching(/Can report schema\? \s+ Yes/));
|
||||
expect(logger.info).toHaveBeenCalledWith(expect.stringMatching(/Can collect usage\? \s+ Yes/));
|
||||
|
|
|
|||
|
|
@ -51,11 +51,15 @@ test('should not leak the exception', async () => {
|
|||
await hive.dispose();
|
||||
|
||||
expect(logger.info).toHaveBeenCalledWith('[hive][reporting] Sending (queue 1) (attempt 1)');
|
||||
expect(logger.info).toHaveBeenCalledWith(expect.stringContaining('[hive][reporting] Attempt 1 failed:'));
|
||||
expect(logger.info).toHaveBeenCalledWith(
|
||||
expect.stringContaining('[hive][reporting] Attempt 1 failed:'),
|
||||
);
|
||||
expect(logger.info).toHaveBeenCalledWith('[hive][reporting] Sending (queue 1) (attempt 2)');
|
||||
expect(logger.error).toHaveBeenCalledTimes(1);
|
||||
expect(logger.error).toHaveBeenCalledWith(
|
||||
expect.stringContaining(`[hive][reporting] Failed to report schema: connect ECONNREFUSED 127.0.0.1:55404`)
|
||||
expect.stringContaining(
|
||||
`[hive][reporting] Failed to report schema: connect ECONNREFUSED 127.0.0.1:55404`,
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -426,7 +430,7 @@ test('should send original schema of a federated service', async () => {
|
|||
type Query {
|
||||
bar: String
|
||||
}
|
||||
`)
|
||||
`),
|
||||
),
|
||||
});
|
||||
|
||||
|
|
@ -498,7 +502,9 @@ test('should display SchemaPublishMissingServiceError', async () => {
|
|||
http.done();
|
||||
|
||||
expect(logger.info).toHaveBeenCalledWith('[hive][reporting] Sending (queue 1) (attempt 1)');
|
||||
expect(logger.error).toHaveBeenCalledWith(`[hive][reporting] Failed to report schema: Service name is not defined`);
|
||||
expect(logger.error).toHaveBeenCalledWith(
|
||||
`[hive][reporting] Failed to report schema: Service name is not defined`,
|
||||
);
|
||||
});
|
||||
|
||||
test('should display SchemaPublishMissingUrlError', async () => {
|
||||
|
|
@ -558,5 +564,7 @@ test('should display SchemaPublishMissingUrlError', async () => {
|
|||
http.done();
|
||||
|
||||
expect(logger.info).toHaveBeenCalledWith('[hive][reporting] Sending (queue 1) (attempt 1)');
|
||||
expect(logger.error).toHaveBeenCalledWith(`[hive][reporting] Failed to report schema: Service url is not defined`);
|
||||
expect(logger.error).toHaveBeenCalledWith(
|
||||
`[hive][reporting] Failed to report schema: Service url is not defined`,
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ test('collect enums and scalars as inputs', async () => {
|
|||
}
|
||||
}
|
||||
`),
|
||||
{}
|
||||
{},
|
||||
).value;
|
||||
|
||||
expect(info.fields).toContain(`Int`);
|
||||
|
|
@ -144,7 +144,7 @@ test('collect enum values from object fields', async () => {
|
|||
}
|
||||
}
|
||||
`),
|
||||
{}
|
||||
{},
|
||||
).value;
|
||||
|
||||
expect(info.fields).toContain(`Int`);
|
||||
|
|
@ -167,7 +167,7 @@ test('collect enum values from arguments', async () => {
|
|||
}
|
||||
}
|
||||
`),
|
||||
{}
|
||||
{},
|
||||
).value;
|
||||
|
||||
expect(info.fields).toContain(`ProjectType.FEDERATION`);
|
||||
|
|
@ -189,7 +189,7 @@ test('collect arguments', async () => {
|
|||
}
|
||||
}
|
||||
`),
|
||||
{}
|
||||
{},
|
||||
).value;
|
||||
|
||||
expect(info.fields).toContain(`Query.projects.filter`);
|
||||
|
|
@ -208,7 +208,7 @@ test('collect used-only input fields', async () => {
|
|||
}
|
||||
}
|
||||
`),
|
||||
{}
|
||||
{},
|
||||
).value;
|
||||
|
||||
expect(info.fields).toContain(`FilterInput.pagination`);
|
||||
|
|
@ -230,7 +230,7 @@ test('collect all input fields when `processVariables` has not been passed and i
|
|||
}
|
||||
}
|
||||
`),
|
||||
{}
|
||||
{},
|
||||
).value;
|
||||
|
||||
expect(info.fields).toContain(`FilterInput.pagination`);
|
||||
|
|
@ -327,7 +327,7 @@ test('(processVariables: true) collect used-only input fields', async () => {
|
|||
limit: 1,
|
||||
},
|
||||
type: 'STITCHING',
|
||||
}
|
||||
},
|
||||
).value;
|
||||
|
||||
expect(info.fields).toContain(`FilterInput.pagination`);
|
||||
|
|
@ -352,7 +352,7 @@ test('(processVariables: true) should collect input object without fields when c
|
|||
`),
|
||||
{
|
||||
type: 'STITCHING',
|
||||
}
|
||||
},
|
||||
).value;
|
||||
|
||||
expect(info.fields).toContain(`FilterInput.type`);
|
||||
|
|
@ -391,7 +391,7 @@ test('(processVariables: true) collect used-only input type fields from an array
|
|||
limit: 10,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
).value;
|
||||
|
||||
expect(info.fields).toContain(`FilterInput.pagination`);
|
||||
|
|
|
|||
|
|
@ -319,10 +319,14 @@ test('should not leak the exception', async () => {
|
|||
await hive.dispose();
|
||||
|
||||
expect(logger.info).toHaveBeenCalledWith(`[hive][usage] Sending (queue 1) (attempt 1)`);
|
||||
expect(logger.info).toHaveBeenCalledWith(expect.stringContaining(`[hive][usage] Attempt 1 failed:`));
|
||||
expect(logger.info).toHaveBeenCalledWith(
|
||||
expect.stringContaining(`[hive][usage] Attempt 1 failed:`),
|
||||
);
|
||||
expect(logger.info).toHaveBeenCalledWith(`[hive][usage] Sending (queue 1) (attempt 2)`);
|
||||
expect(logger.error).toHaveBeenCalledTimes(1);
|
||||
expect(logger.error).toHaveBeenCalledWith(expect.stringContaining(`[hive][usage] Failed to send data`));
|
||||
expect(logger.error).toHaveBeenCalledWith(
|
||||
expect.stringContaining(`[hive][usage] Failed to send data`),
|
||||
);
|
||||
});
|
||||
|
||||
test('sendImmediately should not stop the schedule', async () => {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@
|
|||
|
||||
### Patch Changes
|
||||
|
||||
- [#668](https://github.com/kamilkisiela/graphql-hive/pull/668) [`e116841`](https://github.com/kamilkisiela/graphql-hive/commit/e116841a739bfd7f37c4a826544301cf23d61637) Thanks [@kamilkisiela](https://github.com/kamilkisiela)! - Fix ESM/CJS issue
|
||||
- [#668](https://github.com/kamilkisiela/graphql-hive/pull/668)
|
||||
[`e116841`](https://github.com/kamilkisiela/graphql-hive/commit/e116841a739bfd7f37c4a826544301cf23d61637)
|
||||
Thanks [@kamilkisiela](https://github.com/kamilkisiela)! - Fix ESM/CJS issue
|
||||
|
||||
## 0.2.2
|
||||
|
||||
|
|
|
|||
|
|
@ -1,20 +1,19 @@
|
|||
{
|
||||
"name": "@graphql-hive/core",
|
||||
"version": "0.2.3",
|
||||
"author": {
|
||||
"email": "contact@the-guild.dev",
|
||||
"name": "The Guild",
|
||||
"url": "https://the-guild.dev"
|
||||
},
|
||||
"type": "module",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "kamilkisiela/graphql-hive",
|
||||
"directory": "packages/libraries/core"
|
||||
},
|
||||
"homepage": "https://graphql-hive.com",
|
||||
"author": {
|
||||
"email": "contact@the-guild.dev",
|
||||
"name": "The Guild",
|
||||
"url": "https://the-guild.dev"
|
||||
},
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
"main": "dist/cjs/index.js",
|
||||
"module": "dist/esm/index.js",
|
||||
"exports": {
|
||||
|
|
@ -35,14 +34,6 @@
|
|||
"./package.json": "./package.json"
|
||||
},
|
||||
"typings": "dist/typings/index.d.ts",
|
||||
"typescript": {
|
||||
"definition": "dist/typings/index.d.ts"
|
||||
},
|
||||
"publishConfig": {
|
||||
"registry": "https://registry.npmjs.org",
|
||||
"access": "public",
|
||||
"directory": "dist"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "bob build",
|
||||
"check:build": "bob check"
|
||||
|
|
@ -56,5 +47,14 @@
|
|||
"devDependencies": {
|
||||
"@types/lodash.sortby": "4.7.7",
|
||||
"tslib": "2.4.1"
|
||||
},
|
||||
"publishConfig": {
|
||||
"registry": "https://registry.npmjs.org",
|
||||
"access": "public",
|
||||
"directory": "dist"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"typescript": {
|
||||
"definition": "dist/typings/index.d.ts"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,76 +27,86 @@ export function normalizeOperation({
|
|||
}): string {
|
||||
return stripIgnoredCharacters(
|
||||
print(
|
||||
visit(dropUnusedDefinitions(document, operationName ?? document.definitions.find(isOperationDef)?.name?.value), {
|
||||
// hide literals
|
||||
IntValue(node) {
|
||||
return hideLiterals ? { ...node, value: '0' } : node;
|
||||
visit(
|
||||
dropUnusedDefinitions(
|
||||
document,
|
||||
operationName ?? document.definitions.find(isOperationDef)?.name?.value,
|
||||
),
|
||||
{
|
||||
// hide literals
|
||||
IntValue(node) {
|
||||
return hideLiterals ? { ...node, value: '0' } : node;
|
||||
},
|
||||
FloatValue(node) {
|
||||
return hideLiterals ? { ...node, value: '0' } : node;
|
||||
},
|
||||
StringValue(node) {
|
||||
return hideLiterals ? { ...node, value: '', block: false } : node;
|
||||
},
|
||||
Field(node) {
|
||||
return {
|
||||
...node,
|
||||
// remove aliases
|
||||
alias: removeAliases ? undefined : node.alias,
|
||||
// sort arguments
|
||||
arguments: sortNodes(node.arguments),
|
||||
};
|
||||
},
|
||||
Document(node) {
|
||||
return {
|
||||
...node,
|
||||
definitions: sortNodes(node.definitions),
|
||||
};
|
||||
},
|
||||
OperationDefinition(node) {
|
||||
return {
|
||||
...node,
|
||||
variableDefinitions: sortNodes(node.variableDefinitions),
|
||||
};
|
||||
},
|
||||
SelectionSet(node) {
|
||||
return {
|
||||
...node,
|
||||
selections: sortNodes(node.selections),
|
||||
};
|
||||
},
|
||||
FragmentSpread(node) {
|
||||
return {
|
||||
...node,
|
||||
directives: sortNodes(node.directives),
|
||||
};
|
||||
},
|
||||
InlineFragment(node) {
|
||||
return {
|
||||
...node,
|
||||
directives: sortNodes(node.directives),
|
||||
};
|
||||
},
|
||||
FragmentDefinition(node) {
|
||||
return {
|
||||
...node,
|
||||
directives: sortNodes(node.directives),
|
||||
variableDefinitions: sortNodes(node.variableDefinitions),
|
||||
};
|
||||
},
|
||||
Directive(node) {
|
||||
return { ...node, arguments: sortNodes(node.arguments) };
|
||||
},
|
||||
},
|
||||
FloatValue(node) {
|
||||
return hideLiterals ? { ...node, value: '0' } : node;
|
||||
},
|
||||
StringValue(node) {
|
||||
return hideLiterals ? { ...node, value: '', block: false } : node;
|
||||
},
|
||||
Field(node) {
|
||||
return {
|
||||
...node,
|
||||
// remove aliases
|
||||
alias: removeAliases ? undefined : node.alias,
|
||||
// sort arguments
|
||||
arguments: sortNodes(node.arguments),
|
||||
};
|
||||
},
|
||||
Document(node) {
|
||||
return {
|
||||
...node,
|
||||
definitions: sortNodes(node.definitions),
|
||||
};
|
||||
},
|
||||
OperationDefinition(node) {
|
||||
return {
|
||||
...node,
|
||||
variableDefinitions: sortNodes(node.variableDefinitions),
|
||||
};
|
||||
},
|
||||
SelectionSet(node) {
|
||||
return {
|
||||
...node,
|
||||
selections: sortNodes(node.selections),
|
||||
};
|
||||
},
|
||||
FragmentSpread(node) {
|
||||
return {
|
||||
...node,
|
||||
directives: sortNodes(node.directives),
|
||||
};
|
||||
},
|
||||
InlineFragment(node) {
|
||||
return {
|
||||
...node,
|
||||
directives: sortNodes(node.directives),
|
||||
};
|
||||
},
|
||||
FragmentDefinition(node) {
|
||||
return {
|
||||
...node,
|
||||
directives: sortNodes(node.directives),
|
||||
variableDefinitions: sortNodes(node.variableDefinitions),
|
||||
};
|
||||
},
|
||||
Directive(node) {
|
||||
return { ...node, arguments: sortNodes(node.arguments) };
|
||||
},
|
||||
})
|
||||
)
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
function sortNodes(nodes: readonly DefinitionNode[]): readonly DefinitionNode[];
|
||||
function sortNodes(nodes: readonly SelectionNode[]): readonly SelectionNode[];
|
||||
function sortNodes(nodes: readonly ArgumentNode[] | undefined): readonly ArgumentNode[] | undefined;
|
||||
function sortNodes(nodes: readonly VariableDefinitionNode[] | undefined): readonly VariableDefinitionNode[] | undefined;
|
||||
function sortNodes(nodes: readonly DirectiveNode[] | undefined): readonly DirectiveNode[] | undefined;
|
||||
function sortNodes(
|
||||
nodes: readonly VariableDefinitionNode[] | undefined,
|
||||
): readonly VariableDefinitionNode[] | undefined;
|
||||
function sortNodes(
|
||||
nodes: readonly DirectiveNode[] | undefined,
|
||||
): readonly DirectiveNode[] | undefined;
|
||||
function sortNodes(nodes: readonly any[] | undefined): readonly any[] | undefined {
|
||||
if (nodes) {
|
||||
if (nodes.length === 0) {
|
||||
|
|
@ -115,7 +125,9 @@ function sortNodes(nodes: readonly any[] | undefined): readonly any[] | undefine
|
|||
return sortBy(nodes, 'name.value');
|
||||
}
|
||||
|
||||
if (isOfKindList<SelectionNode>(nodes, [Kind.FIELD, Kind.FRAGMENT_SPREAD, Kind.INLINE_FRAGMENT])) {
|
||||
if (
|
||||
isOfKindList<SelectionNode>(nodes, [Kind.FIELD, Kind.FRAGMENT_SPREAD, Kind.INLINE_FRAGMENT])
|
||||
) {
|
||||
return sortBy(nodes, 'kind', 'name.value');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
# GraphQL Hive - external composition
|
||||
|
||||
[GraphQL Hive](https://graphql-hive.com) is a GraphQL schemas registry where you can host, manage and collaborate on all your GraphQL schemas and operations, compatible with all architecture: schema stitching, federation, or just a good old monolith.
|
||||
[GraphQL Hive](https://graphql-hive.com) is a GraphQL schemas registry where you can host, manage
|
||||
and collaborate on all your GraphQL schemas and operations, compatible with all architecture: schema
|
||||
stitching, federation, or just a good old monolith.
|
||||
|
||||
Read the ["External schema composition"](https://docs.graphql-hive.com/features/external-schema-composition) to learn more.
|
||||
Read the
|
||||
["External schema composition"](https://docs.graphql-hive.com/features/external-schema-composition)
|
||||
to learn more.
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue