mirror of
https://github.com/expo/eas-cli.git
synced 2026-11-15 03:38:08 -10:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f2247f19b | ||
|
|
7a3c782bb5 |
No files matched your search
@@ -13,6 +13,7 @@ This is the log of notable changes to EAS CLI and related packages.
|
|||||||
### 🧹 Chores
|
### 🧹 Chores
|
||||||
|
|
||||||
- Unify channel graphql query types. ([#1949](https://github.com/expo/eas-cli/pull/1949) by [@quinlanj](https://github.com/quinlanj))
|
- Unify channel graphql query types. ([#1949](https://github.com/expo/eas-cli/pull/1949) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
- Fetch entire relay compliant dataset. ([#1953](https://github.com/expo/eas-cli/pull/1953) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
|
||||||
## [3.17.0](https://github.com/expo/eas-cli/releases/tag/v3.17.0) - 2023-07-24
|
## [3.17.0](https://github.com/expo/eas-cli/releases/tag/v3.17.0) - 2023-07-24
|
||||||
|
|
||||||
|
|||||||
Generated
+464
@@ -15554,6 +15554,80 @@
|
|||||||
],
|
],
|
||||||
"possibleTypes": null
|
"possibleTypes": null
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "BuildLimitThresholdExceededMetadata",
|
||||||
|
"description": null,
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "account",
|
||||||
|
"description": null,
|
||||||
|
"args": [],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "Account",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "thresholdsExceeded",
|
||||||
|
"description": null,
|
||||||
|
"args": [],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "LIST",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "NotificationThresholdExceeded",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"inputFields": null,
|
||||||
|
"interfaces": [],
|
||||||
|
"enumValues": null,
|
||||||
|
"possibleTypes": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "ENUM",
|
||||||
|
"name": "BuildLimitThresholdExceededMetadataType",
|
||||||
|
"description": null,
|
||||||
|
"fields": null,
|
||||||
|
"inputFields": null,
|
||||||
|
"interfaces": null,
|
||||||
|
"enumValues": [
|
||||||
|
{
|
||||||
|
"name": "IOS",
|
||||||
|
"description": null,
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "TOTAL",
|
||||||
|
"description": null,
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"possibleTypes": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"kind": "OBJECT",
|
"kind": "OBJECT",
|
||||||
"name": "BuildLogs",
|
"name": "BuildLogs",
|
||||||
@@ -16520,6 +16594,81 @@
|
|||||||
"enumValues": null,
|
"enumValues": null,
|
||||||
"possibleTypes": null
|
"possibleTypes": null
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "BuildPlanCreditThresholdExceededMetadata",
|
||||||
|
"description": null,
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "account",
|
||||||
|
"description": null,
|
||||||
|
"args": [],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "Account",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "buildCreditUsage",
|
||||||
|
"description": null,
|
||||||
|
"args": [],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "Int",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "planLimit",
|
||||||
|
"description": null,
|
||||||
|
"args": [],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "Int",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "threshold",
|
||||||
|
"description": null,
|
||||||
|
"args": [],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "Int",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"inputFields": null,
|
||||||
|
"interfaces": [],
|
||||||
|
"enumValues": null,
|
||||||
|
"possibleTypes": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"kind": "ENUM",
|
"kind": "ENUM",
|
||||||
"name": "BuildPriority",
|
"name": "BuildPriority",
|
||||||
@@ -25965,6 +26114,109 @@
|
|||||||
"enumValues": null,
|
"enumValues": null,
|
||||||
"possibleTypes": null
|
"possibleTypes": null
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "Notification",
|
||||||
|
"description": null,
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "createdAt",
|
||||||
|
"description": null,
|
||||||
|
"args": [],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "DateTime",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "event",
|
||||||
|
"description": null,
|
||||||
|
"args": [],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "ENUM",
|
||||||
|
"name": "NotificationEvent",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"description": null,
|
||||||
|
"args": [],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "ID",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "metadata",
|
||||||
|
"description": null,
|
||||||
|
"args": [],
|
||||||
|
"type": {
|
||||||
|
"kind": "UNION",
|
||||||
|
"name": "NotificationMetadata",
|
||||||
|
"ofType": null
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "type",
|
||||||
|
"description": null,
|
||||||
|
"args": [],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "ENUM",
|
||||||
|
"name": "NotificationType",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "updatedAt",
|
||||||
|
"description": null,
|
||||||
|
"args": [],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "DateTime",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"inputFields": null,
|
||||||
|
"interfaces": [],
|
||||||
|
"enumValues": null,
|
||||||
|
"possibleTypes": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"kind": "ENUM",
|
"kind": "ENUM",
|
||||||
"name": "NotificationEvent",
|
"name": "NotificationEvent",
|
||||||
@@ -25996,10 +26248,42 @@
|
|||||||
"description": null,
|
"description": null,
|
||||||
"isDeprecated": false,
|
"isDeprecated": false,
|
||||||
"deprecationReason": null
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "TEST",
|
||||||
|
"description": null,
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"possibleTypes": null
|
"possibleTypes": null
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"kind": "UNION",
|
||||||
|
"name": "NotificationMetadata",
|
||||||
|
"description": null,
|
||||||
|
"fields": null,
|
||||||
|
"inputFields": null,
|
||||||
|
"interfaces": null,
|
||||||
|
"enumValues": null,
|
||||||
|
"possibleTypes": [
|
||||||
|
{
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "BuildLimitThresholdExceededMetadata",
|
||||||
|
"ofType": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "BuildPlanCreditThresholdExceededMetadata",
|
||||||
|
"ofType": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "TestNotificationMetadata",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"kind": "OBJECT",
|
"kind": "OBJECT",
|
||||||
"name": "NotificationSubscription",
|
"name": "NotificationSubscription",
|
||||||
@@ -26280,6 +26564,81 @@
|
|||||||
"enumValues": null,
|
"enumValues": null,
|
||||||
"possibleTypes": null
|
"possibleTypes": null
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "NotificationThresholdExceeded",
|
||||||
|
"description": null,
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "count",
|
||||||
|
"description": null,
|
||||||
|
"args": [],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "Int",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "limit",
|
||||||
|
"description": null,
|
||||||
|
"args": [],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "Int",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "threshold",
|
||||||
|
"description": null,
|
||||||
|
"args": [],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "Int",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "type",
|
||||||
|
"description": null,
|
||||||
|
"args": [],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "ENUM",
|
||||||
|
"name": "BuildLimitThresholdExceededMetadataType",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"inputFields": null,
|
||||||
|
"interfaces": [],
|
||||||
|
"enumValues": null,
|
||||||
|
"possibleTypes": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"kind": "ENUM",
|
"kind": "ENUM",
|
||||||
"name": "NotificationType",
|
"name": "NotificationType",
|
||||||
@@ -26293,6 +26652,12 @@
|
|||||||
"description": null,
|
"description": null,
|
||||||
"isDeprecated": false,
|
"isDeprecated": false,
|
||||||
"deprecationReason": null
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "WEB",
|
||||||
|
"description": null,
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"possibleTypes": null
|
"possibleTypes": null
|
||||||
@@ -30078,6 +30443,30 @@
|
|||||||
},
|
},
|
||||||
"isDeprecated": false,
|
"isDeprecated": false,
|
||||||
"deprecationReason": null
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "webNotifications",
|
||||||
|
"description": "Web notifications linked to a user",
|
||||||
|
"args": [],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "LIST",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "Notification",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"inputFields": null,
|
"inputFields": null,
|
||||||
@@ -32672,6 +33061,33 @@
|
|||||||
"enumValues": null,
|
"enumValues": null,
|
||||||
"possibleTypes": null
|
"possibleTypes": null
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "TestNotificationMetadata",
|
||||||
|
"description": null,
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "message",
|
||||||
|
"description": null,
|
||||||
|
"args": [],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "String",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"inputFields": null,
|
||||||
|
"interfaces": [],
|
||||||
|
"enumValues": null,
|
||||||
|
"possibleTypes": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"kind": "OBJECT",
|
"kind": "OBJECT",
|
||||||
"name": "TimelineActivityConnection",
|
"name": "TimelineActivityConnection",
|
||||||
@@ -35289,6 +35705,30 @@
|
|||||||
},
|
},
|
||||||
"isDeprecated": false,
|
"isDeprecated": false,
|
||||||
"deprecationReason": null
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "webNotifications",
|
||||||
|
"description": null,
|
||||||
|
"args": [],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "LIST",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "Notification",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"inputFields": null,
|
"inputFields": null,
|
||||||
@@ -35892,6 +36332,30 @@
|
|||||||
},
|
},
|
||||||
"isDeprecated": false,
|
"isDeprecated": false,
|
||||||
"deprecationReason": null
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "webNotifications",
|
||||||
|
"description": "Web notifications linked to a user",
|
||||||
|
"args": [],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "LIST",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "Notification",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"inputFields": null,
|
"inputFields": null,
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { UpdateBranchWithCurrentGroupFragment } from '../../graphql/generated';
|
import { UpdateBranchObject, UpdateChannelObject } from '../../graphql/queries/ChannelQuery';
|
||||||
import { UpdateChannelObject } from '../../graphql/queries/ChannelQuery';
|
|
||||||
|
|
||||||
export const testUpdateBranch1: UpdateBranchWithCurrentGroupFragment = {
|
export const testUpdateBranch1: UpdateBranchObject = {
|
||||||
id: '754bf17f-efc0-46ab-8a59-a03f20e53e9b',
|
id: '754bf17f-efc0-46ab-8a59-a03f20e53e9b',
|
||||||
name: 'wrong-channel',
|
name: 'wrong-channel',
|
||||||
updateGroups: [
|
updateGroups: [
|
||||||
@@ -61,7 +60,7 @@ export const testUpdateBranch1: UpdateBranchWithCurrentGroupFragment = {
|
|||||||
__typename: 'UpdateBranch',
|
__typename: 'UpdateBranch',
|
||||||
};
|
};
|
||||||
|
|
||||||
const testUpdateBranch2: UpdateBranchWithCurrentGroupFragment = {
|
const testUpdateBranch2: UpdateBranchObject = {
|
||||||
id: '6941a8dd-5c0a-48bc-8876-f49c88ed419f',
|
id: '6941a8dd-5c0a-48bc-8876-f49c88ed419f',
|
||||||
name: 'production',
|
name: 'production',
|
||||||
updateGroups: [
|
updateGroups: [
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import { withErrorHandlingAsync } from '../graphql/client';
|
|||||||
import {
|
import {
|
||||||
CreateUpdateChannelOnAppMutation,
|
CreateUpdateChannelOnAppMutation,
|
||||||
CreateUpdateChannelOnAppMutationVariables,
|
CreateUpdateChannelOnAppMutationVariables,
|
||||||
|
PageInfo,
|
||||||
|
UpdateChannelBasicInfoFragment,
|
||||||
ViewBranchesOnUpdateChannelQueryVariables,
|
ViewBranchesOnUpdateChannelQueryVariables,
|
||||||
ViewUpdateChannelsOnAppQueryVariables,
|
ViewUpdateChannelsOnAppQueryVariables,
|
||||||
} from '../graphql/generated';
|
} from '../graphql/generated';
|
||||||
@@ -15,12 +17,14 @@ import { BranchQuery, UpdateBranchOnChannelObject } from '../graphql/queries/Bra
|
|||||||
import { ChannelQuery, UpdateChannelObject } from '../graphql/queries/ChannelQuery';
|
import { ChannelQuery, UpdateChannelObject } from '../graphql/queries/ChannelQuery';
|
||||||
import { UpdateChannelBasicInfoFragmentNode } from '../graphql/types/UpdateChannelBasicInfo';
|
import { UpdateChannelBasicInfoFragmentNode } from '../graphql/types/UpdateChannelBasicInfo';
|
||||||
import Log from '../log';
|
import Log from '../log';
|
||||||
|
import { ora } from '../ora';
|
||||||
import formatFields from '../utils/formatFields';
|
import formatFields from '../utils/formatFields';
|
||||||
import { printJsonOnlyOutput } from '../utils/json';
|
import { printJsonOnlyOutput } from '../utils/json';
|
||||||
import {
|
import {
|
||||||
paginatedQueryWithConfirmPromptAsync,
|
paginatedQueryWithConfirmPromptAsync,
|
||||||
paginatedQueryWithSelectPromptAsync,
|
paginatedQueryWithSelectPromptAsync,
|
||||||
} from '../utils/queries';
|
} from '../utils/queries';
|
||||||
|
import { Connection, QueryParams, getPaginatedDatasetAsync } from '../utils/relay';
|
||||||
import { logChannelDetails } from './utils';
|
import { logChannelDetails } from './utils';
|
||||||
|
|
||||||
export const CHANNELS_LIMIT = 25;
|
export const CHANNELS_LIMIT = 25;
|
||||||
@@ -264,3 +268,46 @@ export async function ensureChannelExistsAsync(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getChannelsDatasetAsync(
|
||||||
|
graphqlClient: ExpoGraphqlClient,
|
||||||
|
{
|
||||||
|
appId,
|
||||||
|
filterPredicate,
|
||||||
|
batchSize = 100,
|
||||||
|
}: {
|
||||||
|
appId: string;
|
||||||
|
filterPredicate?: (channelInfo: UpdateChannelBasicInfoFragment) => boolean;
|
||||||
|
batchSize?: number;
|
||||||
|
}
|
||||||
|
): Promise<UpdateChannelBasicInfoFragment[]> {
|
||||||
|
const queryAsync = async ({
|
||||||
|
first,
|
||||||
|
after,
|
||||||
|
}: QueryParams): Promise<Connection<UpdateChannelBasicInfoFragment>> =>
|
||||||
|
await ChannelQuery.viewUpdateChannelsBasicInfoPaginatedOnAppAsync(graphqlClient, {
|
||||||
|
appId,
|
||||||
|
first,
|
||||||
|
after,
|
||||||
|
});
|
||||||
|
|
||||||
|
const assetSpinner = ora().start('Fetching channels...');
|
||||||
|
const afterEachQuery = (
|
||||||
|
totalNodesFetched: number,
|
||||||
|
_dataset: UpdateChannelBasicInfoFragment[],
|
||||||
|
_batch: UpdateChannelBasicInfoFragment[],
|
||||||
|
pageInfo: PageInfo
|
||||||
|
): void => {
|
||||||
|
if (pageInfo.hasNextPage) {
|
||||||
|
assetSpinner.text = `Fetched ${totalNodesFetched} channels`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const dataset = await getPaginatedDatasetAsync({
|
||||||
|
queryAsync,
|
||||||
|
afterEachQuery,
|
||||||
|
filterPredicate,
|
||||||
|
batchSize,
|
||||||
|
});
|
||||||
|
assetSpinner.succeed(`Fetched all channels`);
|
||||||
|
return dataset;
|
||||||
|
}
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
|
|
||||||
import { UpdateBranchWithCurrentGroupFragment } from '../graphql/generated';
|
import { UpdateBranchObject, UpdateChannelObject } from '../graphql/queries/ChannelQuery';
|
||||||
import { UpdateChannelObject } from '../graphql/queries/ChannelQuery';
|
|
||||||
import Log from '../log';
|
import Log from '../log';
|
||||||
import {
|
import {
|
||||||
FormattedBranchDescription,
|
FormattedBranchDescription,
|
||||||
@@ -114,7 +113,7 @@ export function logChannelDetails(channel: UpdateChannelObject): void {
|
|||||||
function getUpdateBranchNullable(
|
function getUpdateBranchNullable(
|
||||||
channel: UpdateChannelObject,
|
channel: UpdateChannelObject,
|
||||||
branchId: string
|
branchId: string
|
||||||
): UpdateBranchWithCurrentGroupFragment | null {
|
): UpdateBranchObject | null {
|
||||||
const updateBranches = channel.updateBranches;
|
const updateBranches = channel.updateBranches;
|
||||||
const updateBranch = updateBranches.find(branch => branch.id === branchId);
|
const updateBranch = updateBranches.find(branch => branch.id === branchId);
|
||||||
return updateBranch ?? null;
|
return updateBranch ?? null;
|
||||||
@@ -123,7 +122,7 @@ function getUpdateBranchNullable(
|
|||||||
export function getUpdateBranch(
|
export function getUpdateBranch(
|
||||||
channel: UpdateChannelObject,
|
channel: UpdateChannelObject,
|
||||||
branchId: string
|
branchId: string
|
||||||
): UpdateBranchWithCurrentGroupFragment {
|
): UpdateBranchObject {
|
||||||
const updateBranch = getUpdateBranchNullable(channel, branchId);
|
const updateBranch = getUpdateBranchNullable(channel, branchId);
|
||||||
if (!updateBranch) {
|
if (!updateBranch) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
|||||||
+54
-4
@@ -2236,6 +2236,17 @@ export enum BuildJobStatus {
|
|||||||
Started = 'STARTED'
|
Started = 'STARTED'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type BuildLimitThresholdExceededMetadata = {
|
||||||
|
__typename?: 'BuildLimitThresholdExceededMetadata';
|
||||||
|
account: Account;
|
||||||
|
thresholdsExceeded: Array<NotificationThresholdExceeded>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export enum BuildLimitThresholdExceededMetadataType {
|
||||||
|
Ios = 'IOS',
|
||||||
|
Total = 'TOTAL'
|
||||||
|
}
|
||||||
|
|
||||||
export type BuildLogs = {
|
export type BuildLogs = {
|
||||||
__typename?: 'BuildLogs';
|
__typename?: 'BuildLogs';
|
||||||
format?: Maybe<BuildJobLogsFormat>;
|
format?: Maybe<BuildJobLogsFormat>;
|
||||||
@@ -2381,6 +2392,14 @@ export type BuildParamsInput = {
|
|||||||
sdkVersion?: InputMaybe<Scalars['String']>;
|
sdkVersion?: InputMaybe<Scalars['String']>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type BuildPlanCreditThresholdExceededMetadata = {
|
||||||
|
__typename?: 'BuildPlanCreditThresholdExceededMetadata';
|
||||||
|
account: Account;
|
||||||
|
buildCreditUsage: Scalars['Int'];
|
||||||
|
planLimit: Scalars['Int'];
|
||||||
|
threshold: Scalars['Int'];
|
||||||
|
};
|
||||||
|
|
||||||
export enum BuildPriority {
|
export enum BuildPriority {
|
||||||
High = 'HIGH',
|
High = 'HIGH',
|
||||||
Normal = 'NORMAL',
|
Normal = 'NORMAL',
|
||||||
@@ -3758,13 +3777,26 @@ export type MeteredBillingStatus = {
|
|||||||
EAS_UPDATE: Scalars['Boolean'];
|
EAS_UPDATE: Scalars['Boolean'];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type Notification = {
|
||||||
|
__typename?: 'Notification';
|
||||||
|
createdAt: Scalars['DateTime'];
|
||||||
|
event: NotificationEvent;
|
||||||
|
id: Scalars['ID'];
|
||||||
|
metadata?: Maybe<NotificationMetadata>;
|
||||||
|
type: NotificationType;
|
||||||
|
updatedAt: Scalars['DateTime'];
|
||||||
|
};
|
||||||
|
|
||||||
export enum NotificationEvent {
|
export enum NotificationEvent {
|
||||||
BuildComplete = 'BUILD_COMPLETE',
|
BuildComplete = 'BUILD_COMPLETE',
|
||||||
BuildLimitThresholdExceeded = 'BUILD_LIMIT_THRESHOLD_EXCEEDED',
|
BuildLimitThresholdExceeded = 'BUILD_LIMIT_THRESHOLD_EXCEEDED',
|
||||||
BuildPlanCreditThresholdExceeded = 'BUILD_PLAN_CREDIT_THRESHOLD_EXCEEDED',
|
BuildPlanCreditThresholdExceeded = 'BUILD_PLAN_CREDIT_THRESHOLD_EXCEEDED',
|
||||||
SubmissionComplete = 'SUBMISSION_COMPLETE'
|
SubmissionComplete = 'SUBMISSION_COMPLETE',
|
||||||
|
Test = 'TEST'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type NotificationMetadata = BuildLimitThresholdExceededMetadata | BuildPlanCreditThresholdExceededMetadata | TestNotificationMetadata;
|
||||||
|
|
||||||
export type NotificationSubscription = {
|
export type NotificationSubscription = {
|
||||||
__typename?: 'NotificationSubscription';
|
__typename?: 'NotificationSubscription';
|
||||||
account?: Maybe<Account>;
|
account?: Maybe<Account>;
|
||||||
@@ -3805,8 +3837,17 @@ export type NotificationSubscriptionMutationUnsubscribeArgs = {
|
|||||||
id: Scalars['ID'];
|
id: Scalars['ID'];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type NotificationThresholdExceeded = {
|
||||||
|
__typename?: 'NotificationThresholdExceeded';
|
||||||
|
count: Scalars['Int'];
|
||||||
|
limit: Scalars['Int'];
|
||||||
|
threshold: Scalars['Int'];
|
||||||
|
type: BuildLimitThresholdExceededMetadataType;
|
||||||
|
};
|
||||||
|
|
||||||
export enum NotificationType {
|
export enum NotificationType {
|
||||||
Email = 'EMAIL'
|
Email = 'EMAIL',
|
||||||
|
Web = 'WEB'
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Offer = {
|
export type Offer = {
|
||||||
@@ -4309,6 +4350,8 @@ export type SsoUser = Actor & UserActor & {
|
|||||||
/** @deprecated No longer supported */
|
/** @deprecated No longer supported */
|
||||||
twitterUsername?: Maybe<Scalars['String']>;
|
twitterUsername?: Maybe<Scalars['String']>;
|
||||||
username: Scalars['String'];
|
username: Scalars['String'];
|
||||||
|
/** Web notifications linked to a user */
|
||||||
|
webNotifications: Array<Notification>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -4745,6 +4788,11 @@ export type SubscriptionDetailsPlanEnablementArgs = {
|
|||||||
serviceMetric: EasServiceMetric;
|
serviceMetric: EasServiceMetric;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type TestNotificationMetadata = {
|
||||||
|
__typename?: 'TestNotificationMetadata';
|
||||||
|
message: Scalars['String'];
|
||||||
|
};
|
||||||
|
|
||||||
export type TimelineActivityConnection = {
|
export type TimelineActivityConnection = {
|
||||||
__typename?: 'TimelineActivityConnection';
|
__typename?: 'TimelineActivityConnection';
|
||||||
edges: Array<TimelineActivityEdge>;
|
edges: Array<TimelineActivityEdge>;
|
||||||
@@ -5062,6 +5110,7 @@ export type User = Actor & UserActor & {
|
|||||||
/** @deprecated No longer supported */
|
/** @deprecated No longer supported */
|
||||||
twitterUsername?: Maybe<Scalars['String']>;
|
twitterUsername?: Maybe<Scalars['String']>;
|
||||||
username: Scalars['String'];
|
username: Scalars['String'];
|
||||||
|
webNotifications: Array<Notification>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -5150,6 +5199,8 @@ export type UserActor = {
|
|||||||
/** @deprecated No longer supported */
|
/** @deprecated No longer supported */
|
||||||
twitterUsername?: Maybe<Scalars['String']>;
|
twitterUsername?: Maybe<Scalars['String']>;
|
||||||
username: Scalars['String'];
|
username: Scalars['String'];
|
||||||
|
/** Web notifications linked to a user */
|
||||||
|
webNotifications: Array<Notification>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -6173,6 +6224,7 @@ export type ViewBuildsOnAppQuery = { __typename?: 'RootQuery', app: { __typename
|
|||||||
export type ViewUpdateChannelOnAppQueryVariables = Exact<{
|
export type ViewUpdateChannelOnAppQueryVariables = Exact<{
|
||||||
appId: Scalars['String'];
|
appId: Scalars['String'];
|
||||||
channelName: Scalars['String'];
|
channelName: Scalars['String'];
|
||||||
|
filter?: InputMaybe<UpdatesFilter>;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
|
||||||
@@ -6308,8 +6360,6 @@ export type UpdateFragment = { __typename?: 'Update', id: string, group: string,
|
|||||||
|
|
||||||
export type UpdateBranchFragment = { __typename?: 'UpdateBranch', id: string, name: string, updates: Array<{ __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, isRollBackToEmbedded: boolean, manifestPermalink: string, gitCommitHash?: string | null, actor?: { __typename: 'Robot', firstName?: string | null, id: string } | { __typename: 'SSOUser', username: string, id: string } | { __typename: 'User', username: string, id: string } | null, branch: { __typename?: 'UpdateBranch', id: string, name: string }, codeSigningInfo?: { __typename?: 'CodeSigningInfo', keyid: string, sig: string, alg: string } | null }> };
|
export type UpdateBranchFragment = { __typename?: 'UpdateBranch', id: string, name: string, updates: Array<{ __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, isRollBackToEmbedded: boolean, manifestPermalink: string, gitCommitHash?: string | null, actor?: { __typename: 'Robot', firstName?: string | null, id: string } | { __typename: 'SSOUser', username: string, id: string } | { __typename: 'User', username: string, id: string } | null, branch: { __typename?: 'UpdateBranch', id: string, name: string }, codeSigningInfo?: { __typename?: 'CodeSigningInfo', keyid: string, sig: string, alg: string } | null }> };
|
||||||
|
|
||||||
export type UpdateBranchWithCurrentGroupFragment = { __typename?: 'UpdateBranch', id: string, name: string, updateGroups: Array<Array<{ __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, isRollBackToEmbedded: boolean, manifestPermalink: string, gitCommitHash?: string | null, actor?: { __typename: 'Robot', firstName?: string | null, id: string } | { __typename: 'SSOUser', username: string, id: string } | { __typename: 'User', username: string, id: string } | null, branch: { __typename?: 'UpdateBranch', id: string, name: string }, codeSigningInfo?: { __typename?: 'CodeSigningInfo', keyid: string, sig: string, alg: string } | null }>> };
|
|
||||||
|
|
||||||
export type UpdateChannelBasicInfoFragment = { __typename?: 'UpdateChannel', id: string, name: string, branchMapping: string };
|
export type UpdateChannelBasicInfoFragment = { __typename?: 'UpdateChannel', id: string, name: string, branchMapping: string };
|
||||||
|
|
||||||
export type WebhookFragment = { __typename?: 'Webhook', id: string, event: WebhookType, url: string, createdAt: any, updatedAt: any };
|
export type WebhookFragment = { __typename?: 'Webhook', id: string, event: WebhookType, url: string, createdAt: any, updatedAt: any };
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ import { ChannelNotFoundError } from '../../channel/errors';
|
|||||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||||
import { withErrorHandlingAsync } from '../client';
|
import { withErrorHandlingAsync } from '../client';
|
||||||
import {
|
import {
|
||||||
PageInfo,
|
|
||||||
UpdateChannelBasicInfoFragment,
|
|
||||||
ViewUpdateChannelOnAppQuery,
|
ViewUpdateChannelOnAppQuery,
|
||||||
ViewUpdateChannelOnAppQueryVariables,
|
ViewUpdateChannelOnAppQueryVariables,
|
||||||
ViewUpdateChannelsOnAppQuery,
|
ViewUpdateChannelsOnAppQuery,
|
||||||
@@ -14,7 +12,7 @@ import {
|
|||||||
ViewUpdateChannelsPaginatedOnAppQuery,
|
ViewUpdateChannelsPaginatedOnAppQuery,
|
||||||
ViewUpdateChannelsPaginatedOnAppQueryVariables,
|
ViewUpdateChannelsPaginatedOnAppQueryVariables,
|
||||||
} from '../generated';
|
} from '../generated';
|
||||||
import { UpdateBranchWithCurrentGroupFragmentNode } from '../types/UpdateBranchWithCurrentUpdate';
|
import { UpdateFragmentNode } from '../types/Update';
|
||||||
import { UpdateChannelBasicInfoFragmentNode } from '../types/UpdateChannelBasicInfo';
|
import { UpdateChannelBasicInfoFragmentNode } from '../types/UpdateChannelBasicInfo';
|
||||||
|
|
||||||
type ViewUpdateChannelsOnAppObject = NonNullable<
|
type ViewUpdateChannelsOnAppObject = NonNullable<
|
||||||
@@ -28,16 +26,22 @@ type UpdateChannelByNameObject = NonNullable<
|
|||||||
// these types should have the same fields
|
// these types should have the same fields
|
||||||
export type UpdateChannelObject = ViewUpdateChannelsOnAppObject & UpdateChannelByNameObject;
|
export type UpdateChannelObject = ViewUpdateChannelsOnAppObject & UpdateChannelByNameObject;
|
||||||
|
|
||||||
|
export type UpdateBranchObject = UpdateChannelObject['updateBranches'][number];
|
||||||
|
|
||||||
export const ChannelQuery = {
|
export const ChannelQuery = {
|
||||||
async viewUpdateChannelAsync(
|
async viewUpdateChannelAsync(
|
||||||
graphqlClient: ExpoGraphqlClient,
|
graphqlClient: ExpoGraphqlClient,
|
||||||
{ appId, channelName }: ViewUpdateChannelOnAppQueryVariables
|
{ appId, channelName, filter }: ViewUpdateChannelOnAppQueryVariables
|
||||||
): Promise<UpdateChannelByNameObject> {
|
): Promise<UpdateChannelByNameObject> {
|
||||||
const response = await withErrorHandlingAsync(
|
const response = await withErrorHandlingAsync(
|
||||||
graphqlClient
|
graphqlClient
|
||||||
.query<ViewUpdateChannelOnAppQuery, ViewUpdateChannelOnAppQueryVariables>(
|
.query<ViewUpdateChannelOnAppQuery, ViewUpdateChannelOnAppQueryVariables>(
|
||||||
gql`
|
gql`
|
||||||
query ViewUpdateChannelOnApp($appId: String!, $channelName: String!) {
|
query ViewUpdateChannelOnApp(
|
||||||
|
$appId: String!
|
||||||
|
$channelName: String!
|
||||||
|
$filter: UpdatesFilter
|
||||||
|
) {
|
||||||
app {
|
app {
|
||||||
byId(appId: $appId) {
|
byId(appId: $appId) {
|
||||||
id
|
id
|
||||||
@@ -48,15 +52,19 @@ export const ChannelQuery = {
|
|||||||
branchMapping
|
branchMapping
|
||||||
updateBranches(offset: 0, limit: 5) {
|
updateBranches(offset: 0, limit: 5) {
|
||||||
id
|
id
|
||||||
...UpdateBranchWithCurrentGroupFragment
|
name
|
||||||
|
updateGroups(offset: 0, limit: 1, filter: $filter) {
|
||||||
|
id
|
||||||
|
...UpdateFragment
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
${print(UpdateBranchWithCurrentGroupFragmentNode)}
|
${print(UpdateFragmentNode)}
|
||||||
`,
|
`,
|
||||||
{ appId, channelName },
|
{ appId, channelName, filter },
|
||||||
{ additionalTypenames: ['UpdateChannel', 'UpdateBranch', 'Update'] }
|
{ additionalTypenames: ['UpdateChannel', 'UpdateBranch', 'Update'] }
|
||||||
)
|
)
|
||||||
.toPromise()
|
.toPromise()
|
||||||
@@ -88,13 +96,17 @@ export const ChannelQuery = {
|
|||||||
branchMapping
|
branchMapping
|
||||||
updateBranches(offset: 0, limit: 5) {
|
updateBranches(offset: 0, limit: 5) {
|
||||||
id
|
id
|
||||||
...UpdateBranchWithCurrentGroupFragment
|
name
|
||||||
|
updateGroups(offset: 0, limit: 1) {
|
||||||
|
id
|
||||||
|
...UpdateFragment
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
${print(UpdateBranchWithCurrentGroupFragmentNode)}
|
${print(UpdateFragmentNode)}
|
||||||
`,
|
`,
|
||||||
{ appId, offset, limit },
|
{ appId, offset, limit },
|
||||||
{ additionalTypenames: ['UpdateChannel', 'UpdateBranch', 'Update'] }
|
{ additionalTypenames: ['UpdateChannel', 'UpdateBranch', 'Update'] }
|
||||||
@@ -112,7 +124,7 @@ export const ChannelQuery = {
|
|||||||
async viewUpdateChannelsBasicInfoPaginatedOnAppAsync(
|
async viewUpdateChannelsBasicInfoPaginatedOnAppAsync(
|
||||||
graphqlClient: ExpoGraphqlClient,
|
graphqlClient: ExpoGraphqlClient,
|
||||||
{ appId, first, after }: ViewUpdateChannelsPaginatedOnAppQueryVariables
|
{ appId, first, after }: ViewUpdateChannelsPaginatedOnAppQueryVariables
|
||||||
): Promise<[UpdateChannelBasicInfoFragment[], PageInfo]> {
|
): Promise<ViewUpdateChannelsPaginatedOnAppQuery['app']['byId']['channelsPaginated']> {
|
||||||
const response = await withErrorHandlingAsync(
|
const response = await withErrorHandlingAsync(
|
||||||
graphqlClient
|
graphqlClient
|
||||||
.query<
|
.query<
|
||||||
@@ -153,7 +165,6 @@ export const ChannelQuery = {
|
|||||||
if (!channelsPaginated) {
|
if (!channelsPaginated) {
|
||||||
throw new Error(`Could not find channels on project with id ${appId}`);
|
throw new Error(`Could not find channels on project with id ${appId}`);
|
||||||
}
|
}
|
||||||
|
return channelsPaginated;
|
||||||
return [channelsPaginated.edges.map(edge => edge.node) ?? [], channelsPaginated.pageInfo];
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import { print } from 'graphql';
|
|
||||||
import gql from 'graphql-tag';
|
|
||||||
|
|
||||||
import { UpdateFragmentNode } from './Update';
|
|
||||||
|
|
||||||
export const UpdateBranchWithCurrentGroupFragmentNode = gql`
|
|
||||||
fragment UpdateBranchWithCurrentGroupFragment on UpdateBranch {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
updateGroups(offset: 0, limit: 1) {
|
|
||||||
id
|
|
||||||
...UpdateFragment
|
|
||||||
}
|
|
||||||
}
|
|
||||||
${print(UpdateFragmentNode)}
|
|
||||||
`;
|
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
import { getPaginatedDatasetAsync } from '../relay';
|
||||||
|
|
||||||
|
describe('getPaginatedDatasetAsync', () => {
|
||||||
|
const mockQueryAsync = jest.fn(({ first, after }) =>
|
||||||
|
Promise.resolve({
|
||||||
|
edges: Array(first).fill({ node: 'node', cursor: 'cursor' }),
|
||||||
|
pageInfo: {
|
||||||
|
hasNextPage: after !== 'next',
|
||||||
|
endCursor: after ? null : 'next',
|
||||||
|
hasPreviousPage: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should query data page by page until no more data', async () => {
|
||||||
|
const dataset = await getPaginatedDatasetAsync({
|
||||||
|
queryAsync: mockQueryAsync,
|
||||||
|
batchSize: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mockQueryAsync).toHaveBeenCalledTimes(2);
|
||||||
|
expect(mockQueryAsync).toHaveBeenCalledWith({ first: 1, after: undefined });
|
||||||
|
expect(mockQueryAsync).toHaveBeenCalledWith({ first: 1, after: 'next' });
|
||||||
|
expect(dataset).toHaveLength(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call beforeEachQuery and afterEachQuery with correct parameters', async () => {
|
||||||
|
// dataset is pass by reference, so we make a copy at each call
|
||||||
|
const datasetAtNthCall_beforeEachQuery = [] as any;
|
||||||
|
const mockBeforeEachQuery = jest.fn((totalNodesFetched, dataset) => {
|
||||||
|
datasetAtNthCall_beforeEachQuery.push([...dataset]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// dataset is pass by reference, so we make a copy at each call
|
||||||
|
const datasetAtNthCall_afterEachQuery = [] as any;
|
||||||
|
const mockAfterEachQuery = jest.fn((totalNodesFetched, dataset, batch, pageInfo) => {
|
||||||
|
datasetAtNthCall_afterEachQuery.push([...dataset]);
|
||||||
|
});
|
||||||
|
|
||||||
|
await getPaginatedDatasetAsync({
|
||||||
|
queryAsync: mockQueryAsync,
|
||||||
|
beforeEachQuery: mockBeforeEachQuery,
|
||||||
|
afterEachQuery: mockAfterEachQuery,
|
||||||
|
batchSize: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mockBeforeEachQuery).toHaveBeenCalledTimes(2);
|
||||||
|
expect(mockBeforeEachQuery).toHaveBeenNthCalledWith(1, 0, expect.any(Array));
|
||||||
|
expect(datasetAtNthCall_beforeEachQuery[0]).toStrictEqual([]);
|
||||||
|
expect(mockBeforeEachQuery).toHaveBeenNthCalledWith(2, 1, expect.any(Array));
|
||||||
|
expect(datasetAtNthCall_beforeEachQuery[1]).toStrictEqual(['node']);
|
||||||
|
|
||||||
|
expect(mockAfterEachQuery).toHaveBeenCalledTimes(2);
|
||||||
|
expect(mockAfterEachQuery).toHaveBeenNthCalledWith(1, 1, expect.any(Array), ['node'], {
|
||||||
|
hasNextPage: true,
|
||||||
|
endCursor: 'next',
|
||||||
|
hasPreviousPage: false,
|
||||||
|
});
|
||||||
|
expect(datasetAtNthCall_afterEachQuery[0]).toStrictEqual(['node']);
|
||||||
|
expect(mockAfterEachQuery).toHaveBeenNthCalledWith(2, 2, expect.any(Array), ['node'], {
|
||||||
|
hasNextPage: false,
|
||||||
|
endCursor: null,
|
||||||
|
hasPreviousPage: false,
|
||||||
|
});
|
||||||
|
expect(datasetAtNthCall_afterEachQuery[1]).toStrictEqual(['node', 'node']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should filter nodes if filterPredicate is provided', async () => {
|
||||||
|
const filterPredicate = (node: string): boolean => node !== 'node';
|
||||||
|
|
||||||
|
const dataset = await getPaginatedDatasetAsync({
|
||||||
|
queryAsync: mockQueryAsync,
|
||||||
|
filterPredicate,
|
||||||
|
batchSize: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(dataset).toEqual([]); // expecting an empty array because our filterPredicate will filter out 'node'
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should stop fetching when maxNodesFetched limit is reached', async () => {
|
||||||
|
const dataset = await getPaginatedDatasetAsync({
|
||||||
|
queryAsync: mockQueryAsync,
|
||||||
|
batchSize: 2,
|
||||||
|
maxNodesFetched: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(dataset).toHaveLength(2);
|
||||||
|
expect(mockQueryAsync).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
import { PageInfo } from '../graphql/generated';
|
||||||
|
|
||||||
|
export type Connection<T> = {
|
||||||
|
edges: Edge<T>[];
|
||||||
|
pageInfo: PageInfo;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Edge<T> = {
|
||||||
|
node: T;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type QueryParams = {
|
||||||
|
first: number;
|
||||||
|
after?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches dataset in paginated manner (batch by batch) using GraphQL queries.
|
||||||
|
*
|
||||||
|
* @param queryAsync A promise based function for querying.
|
||||||
|
* @param beforeEachQuery Optional. A callback function to be called before each query
|
||||||
|
* @param afterEachQuery Optional. A callback function to be called after each query.
|
||||||
|
* @param filterPredicate Optional. A predicate function to filter the node.
|
||||||
|
* @param batchSize Optional. The batch size of the pagination. Defaults to 100.
|
||||||
|
*
|
||||||
|
* @return {Promise<T[]>} - A promise that resolves to an array (the dataset).
|
||||||
|
* @throws {Error} - If an error occurs during execution of the query or pagination.
|
||||||
|
*/
|
||||||
|
export async function getPaginatedDatasetAsync<T>({
|
||||||
|
queryAsync,
|
||||||
|
beforeEachQuery,
|
||||||
|
afterEachQuery,
|
||||||
|
filterPredicate,
|
||||||
|
batchSize = 100,
|
||||||
|
maxNodesFetched = 10_000,
|
||||||
|
}: {
|
||||||
|
queryAsync: ({ first, after }: QueryParams) => Promise<Connection<T>>;
|
||||||
|
beforeEachQuery?: (totalNodesFetched: number, dataset: T[]) => void;
|
||||||
|
afterEachQuery?: (
|
||||||
|
totalNodesFetched: number,
|
||||||
|
dataset: T[],
|
||||||
|
batch: T[],
|
||||||
|
pageInfo: PageInfo
|
||||||
|
) => void;
|
||||||
|
filterPredicate?: (node: T) => boolean;
|
||||||
|
batchSize?: number;
|
||||||
|
maxNodesFetched?: number;
|
||||||
|
}): Promise<T[]> {
|
||||||
|
const dataset = [];
|
||||||
|
let hasMore = true;
|
||||||
|
let after: string | undefined;
|
||||||
|
let totalNodesFetched = 0;
|
||||||
|
while (hasMore) {
|
||||||
|
if (beforeEachQuery) {
|
||||||
|
beforeEachQuery(totalNodesFetched, dataset);
|
||||||
|
}
|
||||||
|
const result = await queryAsync({ first: batchSize, after });
|
||||||
|
const { edges, pageInfo } = result;
|
||||||
|
const nodes = edges.map(edge => edge.node);
|
||||||
|
const batch = filterPredicate ? nodes.filter(filterPredicate) : nodes;
|
||||||
|
dataset.push(...batch);
|
||||||
|
hasMore = pageInfo.hasNextPage;
|
||||||
|
after = pageInfo.endCursor ?? undefined;
|
||||||
|
totalNodesFetched += nodes.length;
|
||||||
|
if (afterEachQuery) {
|
||||||
|
afterEachQuery(totalNodesFetched, dataset, batch, pageInfo);
|
||||||
|
}
|
||||||
|
if (totalNodesFetched >= maxNodesFetched) {
|
||||||
|
return dataset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dataset;
|
||||||
|
}
|
||||||
Reference in new issue
Block a user