declastruct-github 1.0.7 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/access/daos/DeclaredGithubAppDao.d.ts +8 -0
- package/dist/access/daos/DeclaredGithubAppDao.js +32 -0
- package/dist/access/daos/DeclaredGithubAppDao.js.map +1 -0
- package/dist/access/daos/DeclaredGithubAppInstallationDao.d.ts +9 -0
- package/dist/access/daos/DeclaredGithubAppInstallationDao.js +33 -0
- package/dist/access/daos/DeclaredGithubAppInstallationDao.js.map +1 -0
- package/dist/access/daos/DeclaredGithubBranchDao.js +4 -4
- package/dist/access/daos/DeclaredGithubBranchDao.js.map +1 -1
- package/dist/access/daos/DeclaredGithubBranchProtectionDao.js +4 -4
- package/dist/access/daos/DeclaredGithubBranchProtectionDao.js.map +1 -1
- package/dist/access/daos/DeclaredGithubRepoConfigDao.js +4 -4
- package/dist/access/daos/DeclaredGithubRepoConfigDao.js.map +1 -1
- package/dist/access/daos/DeclaredGithubRepoDao.js +4 -4
- package/dist/access/daos/DeclaredGithubRepoDao.js.map +1 -1
- package/dist/contract/sdks/index.d.ts +4 -0
- package/dist/contract/sdks/index.js +9 -3
- package/dist/contract/sdks/index.js.map +1 -1
- package/dist/domain.objects/DeclaredGithubApp.d.ts +75 -0
- package/dist/domain.objects/DeclaredGithubApp.js +16 -0
- package/dist/domain.objects/DeclaredGithubApp.js.map +1 -0
- package/dist/domain.objects/DeclaredGithubAppInstallation.d.ts +61 -0
- package/dist/domain.objects/DeclaredGithubAppInstallation.js +16 -0
- package/dist/domain.objects/DeclaredGithubAppInstallation.js.map +1 -0
- package/dist/domain.objects/DeclaredGithubAppPermissions.d.ts +126 -0
- package/dist/domain.objects/DeclaredGithubAppPermissions.js +12 -0
- package/dist/domain.objects/DeclaredGithubAppPermissions.js.map +1 -0
- package/dist/domain.objects/DeclaredGithubOwner.d.ts +17 -0
- package/dist/domain.objects/DeclaredGithubOwner.js +8 -0
- package/dist/domain.objects/DeclaredGithubOwner.js.map +1 -0
- package/dist/domain.objects/DeclaredGithubRepoConfig.d.ts +0 -4
- package/dist/domain.objects/DeclaredGithubRepoConfig.js.map +1 -1
- package/dist/domain.objects/DeclastructGithubProvider.d.ts +4 -0
- package/dist/domain.operations/app/castToDeclaredGithubApp.d.ts +12 -0
- package/dist/domain.operations/app/castToDeclaredGithubApp.js +133 -0
- package/dist/domain.operations/app/castToDeclaredGithubApp.js.map +1 -0
- package/dist/domain.operations/app/getOneApp.d.ts +15 -0
- package/dist/domain.operations/app/getOneApp.js +67 -0
- package/dist/domain.operations/app/getOneApp.js.map +1 -0
- package/dist/domain.operations/app/setApp.d.ts +13 -0
- package/dist/domain.operations/app/setApp.js +77 -0
- package/dist/domain.operations/app/setApp.js.map +1 -0
- package/dist/domain.operations/appInstallation/castToDeclaredGithubAppInstallation.d.ts +19 -0
- package/dist/domain.operations/appInstallation/castToDeclaredGithubAppInstallation.js +61 -0
- package/dist/domain.operations/appInstallation/castToDeclaredGithubAppInstallation.js.map +1 -0
- package/dist/domain.operations/appInstallation/deleteAppInstallation.d.ts +11 -0
- package/dist/domain.operations/appInstallation/deleteAppInstallation.js +30 -0
- package/dist/domain.operations/appInstallation/deleteAppInstallation.js.map +1 -0
- package/dist/domain.operations/appInstallation/getOneAppInstallation.d.ts +15 -0
- package/dist/domain.operations/appInstallation/getOneAppInstallation.js +92 -0
- package/dist/domain.operations/appInstallation/getOneAppInstallation.js.map +1 -0
- package/dist/domain.operations/appInstallation/setAppInstallation.d.ts +13 -0
- package/dist/domain.operations/appInstallation/setAppInstallation.js +131 -0
- package/dist/domain.operations/appInstallation/setAppInstallation.js.map +1 -0
- package/dist/domain.operations/branch/setBranch.js +19 -10
- package/dist/domain.operations/branch/setBranch.js.map +1 -1
- package/dist/domain.operations/context/hasContextWithAppToken.d.ts +6 -0
- package/dist/domain.operations/context/hasContextWithAppToken.js +15 -0
- package/dist/domain.operations/context/hasContextWithAppToken.js.map +1 -0
- package/dist/domain.operations/context/hasContextWithPatToken.d.ts +6 -0
- package/dist/domain.operations/context/hasContextWithPatToken.js +17 -0
- package/dist/domain.operations/context/hasContextWithPatToken.js.map +1 -0
- package/dist/domain.operations/provider/getDeclastructGithubProvider.js +4 -0
- package/dist/domain.operations/provider/getDeclastructGithubProvider.js.map +1 -1
- package/dist/domain.operations/repo/getRepos.js +22 -5
- package/dist/domain.operations/repo/getRepos.js.map +1 -1
- package/dist/domain.operations/repoConfig/castToDeclaredGithubRepoConfig.js +0 -1
- package/dist/domain.operations/repoConfig/castToDeclaredGithubRepoConfig.js.map +1 -1
- package/dist/domain.operations/repoConfig/setRepoConfig.js +0 -1
- package/dist/domain.operations/repoConfig/setRepoConfig.js.map +1 -1
- package/package.json +5 -5
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DeclaredGithubOwner = void 0;
|
|
4
|
+
const domain_objects_1 = require("domain-objects");
|
|
5
|
+
class DeclaredGithubOwner extends domain_objects_1.DomainLiteral {
|
|
6
|
+
}
|
|
7
|
+
exports.DeclaredGithubOwner = DeclaredGithubOwner;
|
|
8
|
+
//# sourceMappingURL=DeclaredGithubOwner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DeclaredGithubOwner.js","sourceRoot":"","sources":["../../src/domain.objects/DeclaredGithubOwner.ts"],"names":[],"mappings":";;;AAAA,mDAA+C;AAkB/C,MAAa,mBACX,SAAQ,8BAAkC;CACT;AAFnC,kDAEmC"}
|
|
@@ -27,10 +27,6 @@ export interface DeclaredGithubRepoConfig {
|
|
|
27
27
|
* .what = whether wiki is enabled
|
|
28
28
|
*/
|
|
29
29
|
hasWiki?: boolean;
|
|
30
|
-
/**
|
|
31
|
-
* .what = whether downloads are enabled
|
|
32
|
-
*/
|
|
33
|
-
hasDownloads?: boolean;
|
|
34
30
|
/**
|
|
35
31
|
* .what = whether this is a template repository
|
|
36
32
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DeclaredGithubRepoConfig.js","sourceRoot":"","sources":["../../src/domain.objects/DeclaredGithubRepoConfig.ts"],"names":[],"mappings":";;;AAAA,mDAA2D;
|
|
1
|
+
{"version":3,"file":"DeclaredGithubRepoConfig.js","sourceRoot":"","sources":["../../src/domain.objects/DeclaredGithubRepoConfig.ts"],"names":[],"mappings":";;;AAAA,mDAA2D;AAyG3D,MAAa,wBACX,SAAQ,6BAAsC;;AADhD,4DAQC;AAJe,+BAAM,GAAG,CAAC,MAAM,CAAU,CAAC;AAC3B,+BAAM,GAAG;IACrB,IAAI,EAAE,CAAA,4BAAsC,CAAA;CAC7C,CAAC"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { DeclastructDao, DeclastructProvider } from 'declastruct';
|
|
2
2
|
import type { ContextLogTrail } from 'simple-log-methods';
|
|
3
3
|
import type { ContextGithubApi } from './ContextGithubApi';
|
|
4
|
+
import type { DeclaredGithubApp } from './DeclaredGithubApp';
|
|
5
|
+
import type { DeclaredGithubAppInstallation } from './DeclaredGithubAppInstallation';
|
|
4
6
|
import type { DeclaredGithubBranch } from './DeclaredGithubBranch';
|
|
5
7
|
import type { DeclaredGithubBranchProtection } from './DeclaredGithubBranchProtection';
|
|
6
8
|
import type { DeclaredGithubRepo } from './DeclaredGithubRepo';
|
|
@@ -14,4 +16,6 @@ export type DeclastructGithubProvider = DeclastructProvider<{
|
|
|
14
16
|
DeclaredGithubBranch: DeclastructDao<typeof DeclaredGithubBranch, ContextGithubApi & ContextLogTrail>;
|
|
15
17
|
DeclaredGithubRepoConfig: DeclastructDao<typeof DeclaredGithubRepoConfig, ContextGithubApi & ContextLogTrail>;
|
|
16
18
|
DeclaredGithubBranchProtection: DeclastructDao<typeof DeclaredGithubBranchProtection, ContextGithubApi & ContextLogTrail>;
|
|
19
|
+
DeclaredGithubApp: DeclastructDao<typeof DeclaredGithubApp, ContextGithubApi & ContextLogTrail>;
|
|
20
|
+
DeclaredGithubAppInstallation: DeclastructDao<typeof DeclaredGithubAppInstallation, ContextGithubApi & ContextLogTrail>;
|
|
17
21
|
}, ContextGithubApi & ContextLogTrail>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Endpoints } from '@octokit/types';
|
|
2
|
+
import type { HasMetadata } from 'type-fns';
|
|
3
|
+
import { DeclaredGithubApp } from '../../domain.objects/DeclaredGithubApp';
|
|
4
|
+
type GithubAppResponse = Endpoints['GET /apps/{app_slug}']['response']['data'];
|
|
5
|
+
/**
|
|
6
|
+
* .what = casts GitHub API app response to DeclaredGithubApp
|
|
7
|
+
* .why = transforms external API shape to our domain model with type safety and validation
|
|
8
|
+
*/
|
|
9
|
+
export declare const castToDeclaredGithubApp: (input: GithubAppResponse, options?: {
|
|
10
|
+
inferredPublic?: boolean;
|
|
11
|
+
}) => HasMetadata<DeclaredGithubApp>;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.castToDeclaredGithubApp = void 0;
|
|
4
|
+
const uni_time_1 = require("@ehmpathy/uni-time");
|
|
5
|
+
const helpful_errors_1 = require("helpful-errors");
|
|
6
|
+
const DeclaredGithubApp_1 = require("../../domain.objects/DeclaredGithubApp");
|
|
7
|
+
const DeclaredGithubAppPermissions_1 = require("../../domain.objects/DeclaredGithubAppPermissions");
|
|
8
|
+
const DeclaredGithubOwner_1 = require("../../domain.objects/DeclaredGithubOwner");
|
|
9
|
+
/**
|
|
10
|
+
* .what = casts GitHub API app response to DeclaredGithubApp
|
|
11
|
+
* .why = transforms external API shape to our domain model with type safety and validation
|
|
12
|
+
*/
|
|
13
|
+
const castToDeclaredGithubApp = (input, options) => {
|
|
14
|
+
// fail fast if input is null
|
|
15
|
+
if (!input) {
|
|
16
|
+
helpful_errors_1.UnexpectedCodePathError.throw('app response is null', { input });
|
|
17
|
+
}
|
|
18
|
+
// extract owner from owner object
|
|
19
|
+
const ownerData = input.owner;
|
|
20
|
+
if (!ownerData) {
|
|
21
|
+
helpful_errors_1.UnexpectedCodePathError.throw('owner not found on app response', { input });
|
|
22
|
+
}
|
|
23
|
+
const ownerLogin = 'login' in ownerData ? ownerData.login : null;
|
|
24
|
+
const ownerType = 'type' in ownerData ? ownerData.type : null;
|
|
25
|
+
if (!ownerLogin || !ownerType) {
|
|
26
|
+
helpful_errors_1.UnexpectedCodePathError.throw('owner login/type not found on app response', {
|
|
27
|
+
input,
|
|
28
|
+
ownerData,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
const owner = new DeclaredGithubOwner_1.DeclaredGithubOwner({
|
|
32
|
+
type: ownerType.toLowerCase(),
|
|
33
|
+
slug: ownerLogin,
|
|
34
|
+
});
|
|
35
|
+
// extract required fields
|
|
36
|
+
const id = input.id;
|
|
37
|
+
const slug = input.slug;
|
|
38
|
+
if (!slug) {
|
|
39
|
+
helpful_errors_1.UnexpectedCodePathError.throw('slug not found on app response', { input });
|
|
40
|
+
}
|
|
41
|
+
// cast permissions from snake_case to camelCase with nested structure
|
|
42
|
+
const permissions = castPermissions(input.permissions ?? {});
|
|
43
|
+
return DeclaredGithubApp_1.DeclaredGithubApp.as({
|
|
44
|
+
id,
|
|
45
|
+
slug,
|
|
46
|
+
owner,
|
|
47
|
+
name: input.name ?? null,
|
|
48
|
+
description: input.description
|
|
49
|
+
? input.description
|
|
50
|
+
.split('\n')
|
|
51
|
+
.map((line) => line.trimEnd()) // normalize trailing whitespace per line
|
|
52
|
+
.join('\n')
|
|
53
|
+
.trim() // normalize leading/trailing whitespace
|
|
54
|
+
: null,
|
|
55
|
+
public: (() => {
|
|
56
|
+
// use API response if available
|
|
57
|
+
if ('public' in input)
|
|
58
|
+
return Boolean(input.public);
|
|
59
|
+
// use inferred value if provided
|
|
60
|
+
if (options?.inferredPublic !== undefined)
|
|
61
|
+
return options.inferredPublic;
|
|
62
|
+
// fail fast - never default
|
|
63
|
+
throw new helpful_errors_1.UnexpectedCodePathError('public field not in API response and no inference provided', { input });
|
|
64
|
+
})(),
|
|
65
|
+
permissions,
|
|
66
|
+
events: input.events ?? [],
|
|
67
|
+
homepageUrl: input.external_url ?? null,
|
|
68
|
+
webhookUrl: null, // not exposed in public API response
|
|
69
|
+
createdAt: input.created_at ? (0, uni_time_1.asUniDateTime)(input.created_at) : undefined,
|
|
70
|
+
updatedAt: input.updated_at ? (0, uni_time_1.asUniDateTime)(input.updated_at) : undefined,
|
|
71
|
+
});
|
|
72
|
+
};
|
|
73
|
+
exports.castToDeclaredGithubApp = castToDeclaredGithubApp;
|
|
74
|
+
/**
|
|
75
|
+
* .what = casts GitHub API permissions object to DeclaredGithubAppPermissions
|
|
76
|
+
* .why = transforms snake_case API keys to camelCase domain model with nested repository/organization structure
|
|
77
|
+
*/
|
|
78
|
+
const castPermissions = (input) => {
|
|
79
|
+
// map snake_case keys to camelCase for repository permissions
|
|
80
|
+
const repositoryKeyMap = {
|
|
81
|
+
contents: 'contents',
|
|
82
|
+
pull_requests: 'pullRequests',
|
|
83
|
+
issues: 'issues',
|
|
84
|
+
actions: 'actions',
|
|
85
|
+
administration: 'administration',
|
|
86
|
+
metadata: 'metadata',
|
|
87
|
+
deployments: 'deployments',
|
|
88
|
+
checks: 'checks',
|
|
89
|
+
code_scanning: 'codeScanning',
|
|
90
|
+
secrets: 'secrets',
|
|
91
|
+
workflows: 'workflows',
|
|
92
|
+
environments: 'environments',
|
|
93
|
+
pages: 'pages',
|
|
94
|
+
packages: 'packages',
|
|
95
|
+
repository_hooks: 'hooks',
|
|
96
|
+
};
|
|
97
|
+
// map snake_case keys to camelCase for organization permissions
|
|
98
|
+
const organizationKeyMap = {
|
|
99
|
+
organization_administration: 'administration',
|
|
100
|
+
members: 'members',
|
|
101
|
+
organization_custom_properties: 'customProperties',
|
|
102
|
+
organization_user_blocking: 'userBlocking',
|
|
103
|
+
organization_hooks: 'hooks',
|
|
104
|
+
organization_secrets: 'secrets',
|
|
105
|
+
organization_projects: 'projects',
|
|
106
|
+
organization_self_hosted_runners: 'selfHostedRunners',
|
|
107
|
+
};
|
|
108
|
+
// build the repository permissions object
|
|
109
|
+
const repository = {};
|
|
110
|
+
for (const [snakeKey, value] of Object.entries(input)) {
|
|
111
|
+
const camelKey = repositoryKeyMap[snakeKey];
|
|
112
|
+
if (camelKey && value) {
|
|
113
|
+
repository[camelKey] = value;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// build the organization permissions object
|
|
117
|
+
const organization = {};
|
|
118
|
+
for (const [snakeKey, value] of Object.entries(input)) {
|
|
119
|
+
const camelKey = organizationKeyMap[snakeKey];
|
|
120
|
+
if (camelKey && value) {
|
|
121
|
+
organization[camelKey] = value;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return new DeclaredGithubAppPermissions_1.DeclaredGithubAppPermissions({
|
|
125
|
+
repository: Object.keys(repository).length > 0
|
|
126
|
+
? repository
|
|
127
|
+
: null,
|
|
128
|
+
organization: Object.keys(organization).length > 0
|
|
129
|
+
? organization
|
|
130
|
+
: null,
|
|
131
|
+
});
|
|
132
|
+
};
|
|
133
|
+
//# sourceMappingURL=castToDeclaredGithubApp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"castToDeclaredGithubApp.js","sourceRoot":"","sources":["../../../src/domain.operations/app/castToDeclaredGithubApp.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { RefByUnique } from 'domain-objects';
|
|
2
|
+
import type { HasMetadata, PickOne } from 'type-fns';
|
|
3
|
+
import type { VisualogicContext } from 'visualogic';
|
|
4
|
+
import type { ContextGithubApi } from '../../domain.objects/ContextGithubApi';
|
|
5
|
+
import type { DeclaredGithubApp } from '../../domain.objects/DeclaredGithubApp';
|
|
6
|
+
/**
|
|
7
|
+
* .what = gets a GitHub App by unique key
|
|
8
|
+
* .why = retrieves current state of an app from GitHub API for declarative management
|
|
9
|
+
* .note = by.primary not supported - GitHub API requires app slug or JWT auth to lookup apps
|
|
10
|
+
*/
|
|
11
|
+
export declare const getOneApp: (input: {
|
|
12
|
+
by: PickOne<{
|
|
13
|
+
unique: RefByUnique<typeof DeclaredGithubApp>;
|
|
14
|
+
}>;
|
|
15
|
+
}, context: ContextGithubApi & VisualogicContext) => Promise<HasMetadata<DeclaredGithubApp> | null>;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getOneApp = void 0;
|
|
4
|
+
const rest_1 = require("@octokit/rest");
|
|
5
|
+
const as_procedure_1 = require("as-procedure");
|
|
6
|
+
const helpful_errors_1 = require("helpful-errors");
|
|
7
|
+
const getGithubClient_1 = require("../../access/sdks/getGithubClient");
|
|
8
|
+
const hasContextWithAppToken_1 = require("../context/hasContextWithAppToken");
|
|
9
|
+
const hasContextWithPatToken_1 = require("../context/hasContextWithPatToken");
|
|
10
|
+
const castToDeclaredGithubApp_1 = require("./castToDeclaredGithubApp");
|
|
11
|
+
/**
|
|
12
|
+
* .what = checks if a GitHub App is publicly accessible via unauthenticated request
|
|
13
|
+
* .why = determines public status when API response doesn't include it
|
|
14
|
+
*/
|
|
15
|
+
const checkIfAppIsPublic = async (slug) => {
|
|
16
|
+
const unauthenticatedClient = new rest_1.Octokit();
|
|
17
|
+
try {
|
|
18
|
+
await unauthenticatedClient.apps.getBySlug({ app_slug: slug });
|
|
19
|
+
return true; // accessible without auth = public
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return false; // not accessible without auth = private
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* .what = gets a GitHub App by unique key
|
|
27
|
+
* .why = retrieves current state of an app from GitHub API for declarative management
|
|
28
|
+
* .note = by.primary not supported - GitHub API requires app slug or JWT auth to lookup apps
|
|
29
|
+
*/
|
|
30
|
+
exports.getOneApp = (0, as_procedure_1.asProcedure)(async (input, context) => {
|
|
31
|
+
// get cached GitHub client
|
|
32
|
+
const github = (0, getGithubClient_1.getGithubClient)({}, context);
|
|
33
|
+
// determine app slug from input
|
|
34
|
+
if (!input.by.unique)
|
|
35
|
+
helpful_errors_1.UnexpectedCodePathError.throw('not referenced by unique. how not?', {
|
|
36
|
+
input,
|
|
37
|
+
});
|
|
38
|
+
const { slug } = input.by.unique;
|
|
39
|
+
// detect token type for inferring public field
|
|
40
|
+
const isAppToken = (0, hasContextWithAppToken_1.hasContextWithAppToken)(null, context);
|
|
41
|
+
const isPat = (0, hasContextWithPatToken_1.hasContextWithPatToken)(null, context);
|
|
42
|
+
// execute the GitHub API call
|
|
43
|
+
try {
|
|
44
|
+
const response = await github.apps.getBySlug({ app_slug: slug });
|
|
45
|
+
// infer public status based on token type
|
|
46
|
+
// - app token: if we got a response, app must be public (private apps 404 for other apps' tokens)
|
|
47
|
+
// - PAT: check via unauthenticated request (PAT can see private apps the user owns)
|
|
48
|
+
const inferredPublic = await (async () => {
|
|
49
|
+
if (isAppToken)
|
|
50
|
+
return true;
|
|
51
|
+
if (isPat)
|
|
52
|
+
return checkIfAppIsPublic(slug);
|
|
53
|
+
throw new helpful_errors_1.UnexpectedCodePathError('unsupported token type for inferring public status', { tokenPrefix: context.github.token.slice(0, 10) + '...' });
|
|
54
|
+
})();
|
|
55
|
+
return (0, castToDeclaredGithubApp_1.castToDeclaredGithubApp)(response.data, { inferredPublic });
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
if (!(error instanceof Error))
|
|
59
|
+
throw error;
|
|
60
|
+
// return null for 404/not found
|
|
61
|
+
if (error.message.includes('Not Found'))
|
|
62
|
+
return null;
|
|
63
|
+
// throw helpful error for all other failures
|
|
64
|
+
throw new helpful_errors_1.HelpfulError('github.getOneApp error', { cause: error });
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
//# sourceMappingURL=getOneApp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getOneApp.js","sourceRoot":"","sources":["../../../src/domain.operations/app/getOneApp.ts"],"names":[],"mappings":";;;AAAA,wCAAwC;AACxC,+CAA2C;AAE3C,mDAAuE;AAIvE,uEAAoE;AAGpE,8EAA2E;AAC3E,8EAA2E;AAC3E,uEAAoE;AAEpE;;;GAGG;AACH,MAAM,kBAAkB,GAAG,KAAK,EAAE,IAAY,EAAoB,EAAE;IAClE,MAAM,qBAAqB,GAAG,IAAI,cAAO,EAAE,CAAC;IAC5C,IAAI,CAAC;QACH,MAAM,qBAAqB,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/D,OAAO,IAAI,CAAC,CAAC,mCAAmC;IAClD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC,CAAC,wCAAwC;IACxD,CAAC;AACH,CAAC,CAAC;AAEF;;;;GAIG;AACU,QAAA,SAAS,GAAG,IAAA,0BAAW,EAClC,KAAK,EACH,KAIC,EACD,OAA6C,EACG,EAAE;IAClD,2BAA2B;IAC3B,MAAM,MAAM,GAAG,IAAA,iCAAe,EAAC,EAAE,EAAE,OAAO,CAAC,CAAC;IAE5C,gCAAgC;IAChC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM;QAClB,wCAAuB,CAAC,KAAK,CAAC,oCAAoC,EAAE;YAClE,KAAK;SACN,CAAC,CAAC;IACL,MAAM,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC,MAAM,CAAC;IAEjC,+CAA+C;IAC/C,MAAM,UAAU,GAAG,IAAA,+CAAsB,EAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACzD,MAAM,KAAK,GAAG,IAAA,+CAAsB,EAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAEpD,8BAA8B;IAC9B,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;QAEjE,0CAA0C;QAC1C,kGAAkG;QAClG,oFAAoF;QACpF,MAAM,cAAc,GAAG,MAAM,CAAC,KAAK,IAAI,EAAE;YACvC,IAAI,UAAU;gBAAE,OAAO,IAAI,CAAC;YAC5B,IAAI,KAAK;gBAAE,OAAO,kBAAkB,CAAC,IAAI,CAAC,CAAC;YAC3C,MAAM,IAAI,wCAAuB,CAC/B,oDAAoD,EACpD,EAAE,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,EAAE,CAC3D,CAAC;QACJ,CAAC,CAAC,EAAE,CAAC;QAEL,OAAO,IAAA,iDAAuB,EAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,cAAc,EAAE,CAAC,CAAC;IACpE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,CAAC,KAAK,YAAY,KAAK,CAAC;YAAE,MAAM,KAAK,CAAC;QAE3C,gCAAgC;QAChC,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;YAAE,OAAO,IAAI,CAAC;QAErD,6CAA6C;QAC7C,MAAM,IAAI,6BAAY,CAAC,wBAAwB,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IACrE,CAAC;AACH,CAAC,CACF,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { HasMetadata, PickOne } from 'type-fns';
|
|
2
|
+
import type { VisualogicContext } from 'visualogic';
|
|
3
|
+
import type { ContextGithubApi } from '../../domain.objects/ContextGithubApi';
|
|
4
|
+
import type { DeclaredGithubApp } from '../../domain.objects/DeclaredGithubApp';
|
|
5
|
+
/**
|
|
6
|
+
* .what = sets a GitHub App: finsert or upsert
|
|
7
|
+
* .why = provides declarative interface with helpful errors since apps cannot be created/updated via API
|
|
8
|
+
* .note = GitHub Apps must be created via web UI or manifest flow; this function throws HelpfulError with actionable URLs
|
|
9
|
+
*/
|
|
10
|
+
export declare const setApp: (input: PickOne<{
|
|
11
|
+
finsert: DeclaredGithubApp;
|
|
12
|
+
upsert: DeclaredGithubApp;
|
|
13
|
+
}>, context: ContextGithubApi & VisualogicContext) => Promise<HasMetadata<DeclaredGithubApp>>;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.setApp = void 0;
|
|
4
|
+
const as_procedure_1 = require("as-procedure");
|
|
5
|
+
const helpful_errors_1 = require("helpful-errors");
|
|
6
|
+
const getOneApp_1 = require("./getOneApp");
|
|
7
|
+
/**
|
|
8
|
+
* .what = sets a GitHub App: finsert or upsert
|
|
9
|
+
* .why = provides declarative interface with helpful errors since apps cannot be created/updated via API
|
|
10
|
+
* .note = GitHub Apps must be created via web UI or manifest flow; this function throws HelpfulError with actionable URLs
|
|
11
|
+
*/
|
|
12
|
+
exports.setApp = (0, as_procedure_1.asProcedure)(async (input, context) => {
|
|
13
|
+
const desired = input.finsert ??
|
|
14
|
+
input.upsert ??
|
|
15
|
+
helpful_errors_1.UnexpectedCodePathError.throw('no app provided to setApp', { input });
|
|
16
|
+
// validate that name will cast to expected slug
|
|
17
|
+
// (GitHub auto-generates slug from name during registration)
|
|
18
|
+
const name = desired.name ?? desired.slug;
|
|
19
|
+
const expectedSlug = castNameToSlug(name);
|
|
20
|
+
if (expectedSlug !== desired.slug)
|
|
21
|
+
throw new helpful_errors_1.HelpfulError(`App name "${name}" will generate slug "${expectedSlug}", but expected slug "${desired.slug}".`, {
|
|
22
|
+
suggestion: `Either change the name to "${desired.slug}" or change the slug to "${expectedSlug}"`,
|
|
23
|
+
name,
|
|
24
|
+
expectedSlug,
|
|
25
|
+
desiredSlug: desired.slug,
|
|
26
|
+
});
|
|
27
|
+
// check whether it already exists
|
|
28
|
+
const foundBefore = await (0, getOneApp_1.getOneApp)({
|
|
29
|
+
by: {
|
|
30
|
+
unique: {
|
|
31
|
+
owner: desired.owner,
|
|
32
|
+
slug: desired.slug,
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
}, context);
|
|
36
|
+
// if it's a finsert and exists, return the found app
|
|
37
|
+
if (foundBefore && input.finsert)
|
|
38
|
+
return foundBefore;
|
|
39
|
+
// if it's an upsert and exists, throw HelpfulError with settings URL
|
|
40
|
+
if (foundBefore && input.upsert) {
|
|
41
|
+
const settingsUrl = desired.owner.type === 'organization'
|
|
42
|
+
? `https://github.com/organizations/${desired.owner.slug}/settings/apps/${desired.slug}`
|
|
43
|
+
: `https://github.com/settings/apps/${desired.slug}`;
|
|
44
|
+
throw new helpful_errors_1.HelpfulError('GitHub Apps cannot be updated via API. Please update the app settings manually.', {
|
|
45
|
+
url: settingsUrl,
|
|
46
|
+
instructions: [
|
|
47
|
+
'Navigate to the settings URL above',
|
|
48
|
+
'Update the app permissions, events, or other settings (See the plan diff for which changes to make)',
|
|
49
|
+
'Save your changes',
|
|
50
|
+
],
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
// app doesn't exist - provide manual creation instructions
|
|
54
|
+
const registrationUrl = desired.owner.type === 'organization'
|
|
55
|
+
? `https://github.com/organizations/${desired.owner.slug}/settings/apps/new`
|
|
56
|
+
: `https://github.com/settings/apps/new`;
|
|
57
|
+
throw new helpful_errors_1.HelpfulError('GitHub Apps cannot be created via API. Please create the app manually.', {
|
|
58
|
+
registrationUrl,
|
|
59
|
+
instructions: [
|
|
60
|
+
`1. Navigate to: ${registrationUrl}`,
|
|
61
|
+
`2. Set the app name to: "${desired.name ?? desired.slug}"`,
|
|
62
|
+
'3. Configure permissions and events as shown in the manifest below',
|
|
63
|
+
'4. Save and generate a private key',
|
|
64
|
+
],
|
|
65
|
+
desired,
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
/**
|
|
69
|
+
* .what = casts an app name to the slug GitHub will generate
|
|
70
|
+
* .why = GitHub auto-generates slugs from names during app registration
|
|
71
|
+
* .how = lowercase, replace non-alphanumeric with hyphens, collapse consecutive hyphens, trim hyphens
|
|
72
|
+
*/
|
|
73
|
+
const castNameToSlug = (name) => name
|
|
74
|
+
.toLowerCase()
|
|
75
|
+
.replace(/[^a-z0-9]+/g, '-') // replace non-alphanumeric sequences with single hyphen
|
|
76
|
+
.replace(/^-+|-+$/g, ''); // trim leading/trailing hyphens
|
|
77
|
+
//# sourceMappingURL=setApp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setApp.js","sourceRoot":"","sources":["../../../src/domain.operations/app/setApp.ts"],"names":[],"mappings":";;;AAAA,+CAA2C;AAC3C,mDAAuE;AAMvE,2CAAwC;AAExC;;;;GAIG;AACU,QAAA,MAAM,GAAG,IAAA,0BAAW,EAC/B,KAAK,EACH,KAGE,EACF,OAA6C,EACJ,EAAE;IAC3C,MAAM,OAAO,GACX,KAAK,CAAC,OAAO;QACb,KAAK,CAAC,MAAM;QACZ,wCAAuB,CAAC,KAAK,CAAC,2BAA2B,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;IAExE,gDAAgD;IAChD,6DAA6D;IAC7D,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAC1C,MAAM,YAAY,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;IAC1C,IAAI,YAAY,KAAK,OAAO,CAAC,IAAI;QAC/B,MAAM,IAAI,6BAAY,CACpB,aAAa,IAAI,yBAAyB,YAAY,yBAAyB,OAAO,CAAC,IAAI,IAAI,EAC/F;YACE,UAAU,EAAE,8BAA8B,OAAO,CAAC,IAAI,4BAA4B,YAAY,GAAG;YACjG,IAAI;YACJ,YAAY;YACZ,WAAW,EAAE,OAAO,CAAC,IAAI;SAC1B,CACF,CAAC;IAEJ,kCAAkC;IAClC,MAAM,WAAW,GAAG,MAAM,IAAA,qBAAS,EACjC;QACE,EAAE,EAAE;YACF,MAAM,EAAE;gBACN,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,IAAI,EAAE,OAAO,CAAC,IAAI;aACnB;SACF;KACF,EACD,OAAO,CACR,CAAC;IAEF,qDAAqD;IACrD,IAAI,WAAW,IAAI,KAAK,CAAC,OAAO;QAAE,OAAO,WAAW,CAAC;IAErD,qEAAqE;IACrE,IAAI,WAAW,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QAChC,MAAM,WAAW,GACf,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,cAAc;YACnC,CAAC,CAAC,oCAAoC,OAAO,CAAC,KAAK,CAAC,IAAI,kBAAkB,OAAO,CAAC,IAAI,EAAE;YACxF,CAAC,CAAC,oCAAoC,OAAO,CAAC,IAAI,EAAE,CAAC;QACzD,MAAM,IAAI,6BAAY,CACpB,iFAAiF,EACjF;YACE,GAAG,EAAE,WAAW;YAChB,YAAY,EAAE;gBACZ,oCAAoC;gBACpC,qGAAqG;gBACrG,mBAAmB;aACpB;SACF,CACF,CAAC;IACJ,CAAC;IAED,2DAA2D;IAC3D,MAAM,eAAe,GACnB,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,cAAc;QACnC,CAAC,CAAC,oCAAoC,OAAO,CAAC,KAAK,CAAC,IAAI,oBAAoB;QAC5E,CAAC,CAAC,sCAAsC,CAAC;IAE7C,MAAM,IAAI,6BAAY,CACpB,wEAAwE,EACxE;QACE,eAAe;QACf,YAAY,EAAE;YACZ,mBAAmB,eAAe,EAAE;YACpC,4BAA4B,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI,GAAG;YAC3D,oEAAoE;YACpE,oCAAoC;SACrC;QACD,OAAO;KACR,CACF,CAAC;AACJ,CAAC,CACF,CAAC;AAEF;;;;GAIG;AACH,MAAM,cAAc,GAAG,CAAC,IAAY,EAAU,EAAE,CAC9C,IAAI;KACD,WAAW,EAAE;KACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC,wDAAwD;KACpF,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,CAAC,gCAAgC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Endpoints } from '@octokit/types';
|
|
2
|
+
import type { HasMetadata } from 'type-fns';
|
|
3
|
+
import { DeclaredGithubAppInstallation } from '../../domain.objects/DeclaredGithubAppInstallation';
|
|
4
|
+
import { DeclaredGithubOwner } from '../../domain.objects/DeclaredGithubOwner';
|
|
5
|
+
type GithubInstallationResponse = Endpoints['GET /orgs/{org}/installation']['response']['data'] | Endpoints['GET /users/{username}/installation']['response']['data'] | Endpoints['GET /repos/{owner}/{repo}/installation']['response']['data'] | Endpoints['GET /app/installations/{installation_id}']['response']['data'];
|
|
6
|
+
/**
|
|
7
|
+
* .what = casts GitHub API installation response to DeclaredGithubAppInstallation
|
|
8
|
+
* .why = transforms external API shape to our domain model with type safety and validation
|
|
9
|
+
* .note = requires app reference to be passed in since installation response doesn't include app owner
|
|
10
|
+
*/
|
|
11
|
+
export declare const castToDeclaredGithubAppInstallation: (input: {
|
|
12
|
+
installation: GithubInstallationResponse;
|
|
13
|
+
app: {
|
|
14
|
+
owner: DeclaredGithubOwner;
|
|
15
|
+
slug: string;
|
|
16
|
+
};
|
|
17
|
+
repositories: string[] | null;
|
|
18
|
+
}) => HasMetadata<DeclaredGithubAppInstallation>;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.castToDeclaredGithubAppInstallation = void 0;
|
|
4
|
+
const uni_time_1 = require("@ehmpathy/uni-time");
|
|
5
|
+
const domain_objects_1 = require("domain-objects");
|
|
6
|
+
const helpful_errors_1 = require("helpful-errors");
|
|
7
|
+
const DeclaredGithubApp_1 = require("../../domain.objects/DeclaredGithubApp");
|
|
8
|
+
const DeclaredGithubAppInstallation_1 = require("../../domain.objects/DeclaredGithubAppInstallation");
|
|
9
|
+
const DeclaredGithubOwner_1 = require("../../domain.objects/DeclaredGithubOwner");
|
|
10
|
+
/**
|
|
11
|
+
* .what = casts GitHub API installation response to DeclaredGithubAppInstallation
|
|
12
|
+
* .why = transforms external API shape to our domain model with type safety and validation
|
|
13
|
+
* .note = requires app reference to be passed in since installation response doesn't include app owner
|
|
14
|
+
*/
|
|
15
|
+
const castToDeclaredGithubAppInstallation = (input) => {
|
|
16
|
+
const { installation, app } = input;
|
|
17
|
+
// extract account info
|
|
18
|
+
const account = installation.account;
|
|
19
|
+
if (!account)
|
|
20
|
+
helpful_errors_1.UnexpectedCodePathError.throw('installation account not found', {
|
|
21
|
+
installation,
|
|
22
|
+
});
|
|
23
|
+
// extract login from account (type narrowing)
|
|
24
|
+
const targetSlug = 'login' in account ? account.login : undefined;
|
|
25
|
+
if (!targetSlug)
|
|
26
|
+
helpful_errors_1.UnexpectedCodePathError.throw('installation account login not found', {
|
|
27
|
+
installation,
|
|
28
|
+
account,
|
|
29
|
+
});
|
|
30
|
+
// extract target type
|
|
31
|
+
const targetType = installation.target_type;
|
|
32
|
+
if (!targetType)
|
|
33
|
+
helpful_errors_1.UnexpectedCodePathError.throw('installation target_type not found', {
|
|
34
|
+
installation,
|
|
35
|
+
});
|
|
36
|
+
// compose target object
|
|
37
|
+
const target = new DeclaredGithubOwner_1.DeclaredGithubOwner({
|
|
38
|
+
type: targetType.toLowerCase(),
|
|
39
|
+
slug: targetSlug,
|
|
40
|
+
});
|
|
41
|
+
// extract repository selection
|
|
42
|
+
const repositorySelection = (installation.repository_selection ?? 'all');
|
|
43
|
+
return DeclaredGithubAppInstallation_1.DeclaredGithubAppInstallation.as({
|
|
44
|
+
id: installation.id,
|
|
45
|
+
app: app instanceof DeclaredGithubApp_1.DeclaredGithubApp
|
|
46
|
+
? (0, domain_objects_1.refByUnique)(app)
|
|
47
|
+
: app,
|
|
48
|
+
target,
|
|
49
|
+
repositorySelection,
|
|
50
|
+
repositories: input.repositories,
|
|
51
|
+
suspended: installation.suspended_at !== null,
|
|
52
|
+
createdAt: installation.created_at
|
|
53
|
+
? (0, uni_time_1.asUniDateTime)(installation.created_at)
|
|
54
|
+
: undefined,
|
|
55
|
+
updatedAt: installation.updated_at
|
|
56
|
+
? (0, uni_time_1.asUniDateTime)(installation.updated_at)
|
|
57
|
+
: undefined,
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
exports.castToDeclaredGithubAppInstallation = castToDeclaredGithubAppInstallation;
|
|
61
|
+
//# sourceMappingURL=castToDeclaredGithubAppInstallation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"castToDeclaredGithubAppInstallation.js","sourceRoot":"","sources":["../../../src/domain.operations/appInstallation/castToDeclaredGithubAppInstallation.ts"],"names":[],"mappings":";;;AAAA,iDAAmD;AAEnD,mDAA6C;AAC7C,mDAAyD;AAGzD,8EAA2E;AAC3E,sGAAmG;AACnG,kFAA+E;AAQ/E;;;;GAIG;AACI,MAAM,mCAAmC,GAAG,CAAC,KAInD,EAA8C,EAAE;IAC/C,MAAM,EAAE,YAAY,EAAE,GAAG,EAAE,GAAG,KAAK,CAAC;IAEpC,uBAAuB;IACvB,MAAM,OAAO,GAAG,YAAY,CAAC,OAAO,CAAC;IACrC,IAAI,CAAC,OAAO;QACV,wCAAuB,CAAC,KAAK,CAAC,gCAAgC,EAAE;YAC9D,YAAY;SACb,CAAC,CAAC;IAEL,8CAA8C;IAC9C,MAAM,UAAU,GAAG,OAAO,IAAI,OAAO,CAAC,CAAC,CAAE,OAAO,CAAC,KAAgB,CAAC,CAAC,CAAC,SAAS,CAAC;IAC9E,IAAI,CAAC,UAAU;QACb,wCAAuB,CAAC,KAAK,CAAC,sCAAsC,EAAE;YACpE,YAAY;YACZ,OAAO;SACR,CAAC,CAAC;IAEL,sBAAsB;IACtB,MAAM,UAAU,GAAG,YAAY,CAAC,WAAW,CAAC;IAC5C,IAAI,CAAC,UAAU;QACb,wCAAuB,CAAC,KAAK,CAAC,oCAAoC,EAAE;YAClE,YAAY;SACb,CAAC,CAAC;IAEL,wBAAwB;IACxB,MAAM,MAAM,GAAG,IAAI,yCAAmB,CAAC;QACrC,IAAI,EAAE,UAAU,CAAC,WAAW,EAA6B;QACzD,IAAI,EAAE,UAAU;KACjB,CAAC,CAAC;IAEH,+BAA+B;IAC/B,MAAM,mBAAmB,GAAG,CAAC,YAAY,CAAC,oBAAoB,IAAI,KAAK,CAEzD,CAAC;IAEf,OAAO,6DAA6B,CAAC,EAAE,CAAC;QACtC,EAAE,EAAE,YAAY,CAAC,EAAE;QACnB,GAAG,EACD,GAAG,YAAY,qCAAiB;YAC9B,CAAC,CAAC,IAAA,4BAAW,EAA2B,GAAG,CAAC;YAC5C,CAAC,CAAC,GAAG;QACT,MAAM;QACN,mBAAmB;QACnB,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,SAAS,EAAE,YAAY,CAAC,YAAY,KAAK,IAAI;QAC7C,SAAS,EAAE,YAAY,CAAC,UAAU;YAChC,CAAC,CAAC,IAAA,wBAAa,EAAC,YAAY,CAAC,UAAU,CAAC;YACxC,CAAC,CAAC,SAAS;QACb,SAAS,EAAE,YAAY,CAAC,UAAU;YAChC,CAAC,CAAC,IAAA,wBAAa,EAAC,YAAY,CAAC,UAAU,CAAC;YACxC,CAAC,CAAC,SAAS;KACd,CAA+C,CAAC;AACnD,CAAC,CAAC;AAzDW,QAAA,mCAAmC,uCAyD9C"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { VisualogicContext } from 'visualogic';
|
|
2
|
+
import type { ContextGithubApi } from '../../domain.objects/ContextGithubApi';
|
|
3
|
+
import type { DeclaredGithubAppInstallation } from '../../domain.objects/DeclaredGithubAppInstallation';
|
|
4
|
+
/**
|
|
5
|
+
* .what = deletes a GitHub App installation
|
|
6
|
+
* .why = provides declarative interface with helpful error since deletion requires App JWT
|
|
7
|
+
* .note = DELETE /app/installations/{id} requires App JWT which is out of scope; throws HelpfulError with uninstall URL
|
|
8
|
+
*/
|
|
9
|
+
export declare const deleteAppInstallation: (input: {
|
|
10
|
+
installation: DeclaredGithubAppInstallation;
|
|
11
|
+
}, _context: ContextGithubApi & VisualogicContext) => Promise<never>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deleteAppInstallation = void 0;
|
|
4
|
+
const as_procedure_1 = require("as-procedure");
|
|
5
|
+
const helpful_errors_1 = require("helpful-errors");
|
|
6
|
+
/**
|
|
7
|
+
* .what = deletes a GitHub App installation
|
|
8
|
+
* .why = provides declarative interface with helpful error since deletion requires App JWT
|
|
9
|
+
* .note = DELETE /app/installations/{id} requires App JWT which is out of scope; throws HelpfulError with uninstall URL
|
|
10
|
+
*/
|
|
11
|
+
exports.deleteAppInstallation = (0, as_procedure_1.asProcedure)(async (input, _context) => {
|
|
12
|
+
const { installation } = input;
|
|
13
|
+
// construct the uninstall URL based on target type
|
|
14
|
+
const uninstallUrl = installation.target.type === 'organization'
|
|
15
|
+
? `https://github.com/organizations/${installation.target.slug}/settings/installations`
|
|
16
|
+
: `https://github.com/settings/installations`;
|
|
17
|
+
throw new helpful_errors_1.HelpfulError('GitHub App installations cannot be deleted via API with a user token. Please uninstall the app manually.', {
|
|
18
|
+
uninstallUrl,
|
|
19
|
+
instructions: [
|
|
20
|
+
`1. Navigate to: ${uninstallUrl}`,
|
|
21
|
+
`2. Find the installation for app "${installation.app.slug}"`,
|
|
22
|
+
'3. Click "Configure" next to the installation',
|
|
23
|
+
'4. Scroll to the "Danger zone" section',
|
|
24
|
+
'5. Click "Uninstall" to remove the installation',
|
|
25
|
+
],
|
|
26
|
+
note: 'Deletion via API requires an App JWT (authentication as the GitHub App itself), which is out of scope for this SDK.',
|
|
27
|
+
installation,
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
//# sourceMappingURL=deleteAppInstallation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deleteAppInstallation.js","sourceRoot":"","sources":["../../../src/domain.operations/appInstallation/deleteAppInstallation.ts"],"names":[],"mappings":";;;AAAA,+CAA2C;AAC3C,mDAA8C;AAM9C;;;;GAIG;AACU,QAAA,qBAAqB,GAAG,IAAA,0BAAW,EAC9C,KAAK,EACH,KAEC,EACD,QAA8C,EAC9B,EAAE;IAClB,MAAM,EAAE,YAAY,EAAE,GAAG,KAAK,CAAC;IAE/B,mDAAmD;IACnD,MAAM,YAAY,GAChB,YAAY,CAAC,MAAM,CAAC,IAAI,KAAK,cAAc;QACzC,CAAC,CAAC,oCAAoC,YAAY,CAAC,MAAM,CAAC,IAAI,yBAAyB;QACvF,CAAC,CAAC,2CAA2C,CAAC;IAElD,MAAM,IAAI,6BAAY,CACpB,0GAA0G,EAC1G;QACE,YAAY;QACZ,YAAY,EAAE;YACZ,mBAAmB,YAAY,EAAE;YACjC,qCAAqC,YAAY,CAAC,GAAG,CAAC,IAAI,GAAG;YAC7D,+CAA+C;YAC/C,wCAAwC;YACxC,iDAAiD;SAClD;QACD,IAAI,EAAE,qHAAqH;QAC3H,YAAY;KACb,CACF,CAAC;AACJ,CAAC,CACF,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { RefByUnique } from 'domain-objects';
|
|
2
|
+
import type { HasMetadata } from 'type-fns';
|
|
3
|
+
import type { VisualogicContext } from 'visualogic';
|
|
4
|
+
import type { ContextGithubApi } from '../../domain.objects/ContextGithubApi';
|
|
5
|
+
import type { DeclaredGithubAppInstallation } from '../../domain.objects/DeclaredGithubAppInstallation';
|
|
6
|
+
/**
|
|
7
|
+
* .what = gets a GitHub App installation by unique key
|
|
8
|
+
* .why = retrieves current state of an installation from GitHub API for declarative management
|
|
9
|
+
* .note = lookup by primary key (installation id) is not supported - requires App JWT auth
|
|
10
|
+
*/
|
|
11
|
+
export declare const getOneAppInstallation: (input: {
|
|
12
|
+
by: {
|
|
13
|
+
unique: RefByUnique<typeof DeclaredGithubAppInstallation>;
|
|
14
|
+
};
|
|
15
|
+
}, context: ContextGithubApi & VisualogicContext) => Promise<HasMetadata<DeclaredGithubAppInstallation> | null>;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getOneAppInstallation = void 0;
|
|
4
|
+
const as_procedure_1 = require("as-procedure");
|
|
5
|
+
const helpful_errors_1 = require("helpful-errors");
|
|
6
|
+
const getGithubClient_1 = require("../../access/sdks/getGithubClient");
|
|
7
|
+
const hasContextWithAppToken_1 = require("../context/hasContextWithAppToken");
|
|
8
|
+
const hasContextWithPatToken_1 = require("../context/hasContextWithPatToken");
|
|
9
|
+
const castToDeclaredGithubAppInstallation_1 = require("./castToDeclaredGithubAppInstallation");
|
|
10
|
+
/**
|
|
11
|
+
* .what = gets a GitHub App installation by unique key
|
|
12
|
+
* .why = retrieves current state of an installation from GitHub API for declarative management
|
|
13
|
+
* .note = lookup by primary key (installation id) is not supported - requires App JWT auth
|
|
14
|
+
*/
|
|
15
|
+
exports.getOneAppInstallation = (0, as_procedure_1.asProcedure)(async (input, context) => {
|
|
16
|
+
// get cached GitHub client
|
|
17
|
+
const github = (0, getGithubClient_1.getGithubClient)({}, context);
|
|
18
|
+
// handle get by unique key
|
|
19
|
+
if (input.by.unique) {
|
|
20
|
+
const { app, target } = input.by.unique;
|
|
21
|
+
// list installations for the org/user and filter by app
|
|
22
|
+
// uses GET /orgs/{org}/installations which works with PAT (admin:read scope)
|
|
23
|
+
try {
|
|
24
|
+
if (target.type === 'organization') {
|
|
25
|
+
const response = await github.orgs.listAppInstallations({
|
|
26
|
+
org: target.slug,
|
|
27
|
+
per_page: 100,
|
|
28
|
+
});
|
|
29
|
+
// find installation matching app slug
|
|
30
|
+
const installation = response.data.installations.find((inst) => inst.app_slug === app.slug);
|
|
31
|
+
if (!installation)
|
|
32
|
+
return null;
|
|
33
|
+
// fetch repositories if selection is 'selected'
|
|
34
|
+
// (the list endpoint doesn't include repositories)
|
|
35
|
+
let repositories = null;
|
|
36
|
+
if (installation.repository_selection === 'selected') {
|
|
37
|
+
// detect token type
|
|
38
|
+
const isPatToken = (0, hasContextWithPatToken_1.hasContextWithPatToken)(null, context);
|
|
39
|
+
const isAppToken = (0, hasContextWithAppToken_1.hasContextWithAppToken)(null, context);
|
|
40
|
+
// fail fast on unknown token type
|
|
41
|
+
if (!isPatToken && !isAppToken) {
|
|
42
|
+
throw new helpful_errors_1.HelpfulError('Unknown token type. Expected PAT (ghp_* or github_pat_*) or installation token (ghs_*).', { tokenPrefix: context.github.token.slice(0, 10) + '...' });
|
|
43
|
+
}
|
|
44
|
+
// PAT: GET /user/installations/{id}/repositories
|
|
45
|
+
if (isPatToken) {
|
|
46
|
+
const reposResponse = await github.apps.listInstallationReposForAuthenticatedUser({
|
|
47
|
+
installation_id: installation.id,
|
|
48
|
+
per_page: 100,
|
|
49
|
+
});
|
|
50
|
+
repositories = reposResponse.data.repositories.map((r) => r.name);
|
|
51
|
+
}
|
|
52
|
+
// installation token: GET /installation/repositories
|
|
53
|
+
if (isAppToken) {
|
|
54
|
+
const reposResponse = await github.apps.listReposAccessibleToInstallation({
|
|
55
|
+
per_page: 100,
|
|
56
|
+
});
|
|
57
|
+
repositories = reposResponse.data.repositories.map((r) => r.name);
|
|
58
|
+
}
|
|
59
|
+
// fail fast if repositories weren't fetched
|
|
60
|
+
if (repositories === null) {
|
|
61
|
+
throw new helpful_errors_1.UnexpectedCodePathError('repositories not fetched for selected installation', { isPatToken, isAppToken, installation_id: installation.id });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return (0, castToDeclaredGithubAppInstallation_1.castToDeclaredGithubAppInstallation)({
|
|
65
|
+
installation,
|
|
66
|
+
app,
|
|
67
|
+
repositories,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
// for User targets, there's no PAT-accessible endpoint
|
|
71
|
+
if (target.type === 'user') {
|
|
72
|
+
throw new helpful_errors_1.HelpfulError('User installations are not supported. GitHub API has no PAT-accessible endpoint to read user installations.', { target });
|
|
73
|
+
}
|
|
74
|
+
// for unsupported target.type, fail fast
|
|
75
|
+
throw new helpful_errors_1.UnexpectedCodePathError('unsupported target.type', { input });
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
if (!(error instanceof Error))
|
|
79
|
+
throw error;
|
|
80
|
+
if (error.message.includes('Not Found'))
|
|
81
|
+
return null;
|
|
82
|
+
throw new helpful_errors_1.HelpfulError('github.getOneAppInstallation.byUnique error', {
|
|
83
|
+
cause: error,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// no valid key provided
|
|
88
|
+
helpful_errors_1.UnexpectedCodePathError.throw('getOneAppInstallation requires by.unique', {
|
|
89
|
+
input,
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
//# sourceMappingURL=getOneAppInstallation.js.map
|