@quiltdata/benchling-webhook 0.4.13
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/AGENTS.md +226 -0
- package/CHANGELOG.md +91 -0
- package/LICENSE +201 -0
- package/README.benchling.md +77 -0
- package/README.md +53 -0
- package/bin/benchling-webhook.ts +172 -0
- package/bin/check-logs.js +231 -0
- package/bin/cli-auth.sh +74 -0
- package/bin/get-env.js +564 -0
- package/bin/publish-manual.js +211 -0
- package/bin/release-notes.sh +82 -0
- package/bin/release.js +118 -0
- package/bin/send-event.js +203 -0
- package/bin/sync-version.js +72 -0
- package/bin/test-invalid-signature.js +125 -0
- package/bin/version.js +178 -0
- package/cdk.context.json +58 -0
- package/cdk.json +85 -0
- package/doc/NPM_OIDC_SETUP.md +95 -0
- package/doc/PARAMETERS.md +203 -0
- package/doc/RELEASE.md +297 -0
- package/doc/RELEASE_NOTES.md +64 -0
- package/env.template +67 -0
- package/jest.config.js +14 -0
- package/lib/README.md +50 -0
- package/lib/index.ts +31 -0
- package/lib/oauth-tester.json +35 -0
- package/package.json +79 -0
- package/tsconfig.json +34 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import "dotenv/config";
|
|
3
|
+
import * as cdk from "aws-cdk-lib";
|
|
4
|
+
import { BenchlingWebhookStack } from "../lib/benchling-webhook-stack";
|
|
5
|
+
|
|
6
|
+
// Import get-env to infer configuration from catalog
|
|
7
|
+
const { inferStackConfig } = require("./get-env.js");
|
|
8
|
+
const { execSync } = require("child_process");
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Check if CDK is bootstrapped for the given account/region
|
|
12
|
+
*/
|
|
13
|
+
async function checkCdkBootstrap(account: string, region: string): Promise<void> {
|
|
14
|
+
try {
|
|
15
|
+
console.log(`Checking CDK bootstrap for account ${account} in ${region}...`);
|
|
16
|
+
|
|
17
|
+
// Check if bootstrap stack exists
|
|
18
|
+
const result = execSync(
|
|
19
|
+
`aws cloudformation describe-stacks --region ${region} --stack-name CDKToolkit --query "Stacks[0].StackStatus" --output text 2>&1`,
|
|
20
|
+
{ encoding: "utf-8" }
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
const stackStatus = result.trim();
|
|
24
|
+
|
|
25
|
+
if (stackStatus.includes("does not exist") || stackStatus.includes("ValidationError")) {
|
|
26
|
+
console.error("\n❌ CDK Bootstrap Error");
|
|
27
|
+
console.error("=".repeat(80));
|
|
28
|
+
console.error(`CDK is not bootstrapped for account ${account} in region ${region}`);
|
|
29
|
+
console.error("\nTo bootstrap CDK, run:");
|
|
30
|
+
console.error(` npx cdk bootstrap aws://${account}/${region}`);
|
|
31
|
+
console.error("\nOr source your .env and run:");
|
|
32
|
+
console.error(` source .env`);
|
|
33
|
+
console.error(` npx cdk bootstrap aws://\${CDK_DEFAULT_ACCOUNT}/\${CDK_DEFAULT_REGION}`);
|
|
34
|
+
console.error("=".repeat(80));
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Check if the stack is in a good state
|
|
39
|
+
if (!stackStatus.includes("COMPLETE")) {
|
|
40
|
+
console.error("\n⚠️ CDK Bootstrap Warning");
|
|
41
|
+
console.error("=".repeat(80));
|
|
42
|
+
console.error(`CDKToolkit stack is in state: ${stackStatus}`);
|
|
43
|
+
console.error("This may cause deployment issues.");
|
|
44
|
+
console.error("=".repeat(80));
|
|
45
|
+
} else {
|
|
46
|
+
console.log(`✓ CDK is bootstrapped (CDKToolkit stack: ${stackStatus})\n`);
|
|
47
|
+
}
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.error("\n⚠️ Warning: Could not verify CDK bootstrap status");
|
|
50
|
+
console.error(`Error: ${(error as Error).message}`);
|
|
51
|
+
console.error("\nProceeding anyway, but deployment may fail if CDK is not bootstrapped.\n");
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Get environment configuration with catalog inference
|
|
57
|
+
*
|
|
58
|
+
* This combines user-provided values from .env with inferred values from the Quilt catalog.
|
|
59
|
+
* User values always take precedence over inferred values.
|
|
60
|
+
*/
|
|
61
|
+
async function getConfig() {
|
|
62
|
+
const userEnv = process.env;
|
|
63
|
+
let inferredEnv: Record<string, string> = {};
|
|
64
|
+
|
|
65
|
+
// If QUILT_CATALOG is provided, try to infer additional configuration
|
|
66
|
+
if (userEnv.QUILT_CATALOG) {
|
|
67
|
+
try {
|
|
68
|
+
console.log(`Inferring configuration from catalog: ${userEnv.QUILT_CATALOG}`);
|
|
69
|
+
const result = await inferStackConfig(`https://${userEnv.QUILT_CATALOG.replace(/^https?:\/\//, '')}`);
|
|
70
|
+
inferredEnv = result.inferredVars;
|
|
71
|
+
console.log("✓ Successfully inferred stack configuration\n");
|
|
72
|
+
} catch (error) {
|
|
73
|
+
console.error(`Warning: Could not infer configuration from catalog: ${(error as Error).message}`);
|
|
74
|
+
console.error("Falling back to environment variables only.\n");
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Merge: user env takes precedence over inferred values
|
|
79
|
+
const config = { ...inferredEnv, ...userEnv };
|
|
80
|
+
|
|
81
|
+
// Validate required user-provided values
|
|
82
|
+
const requiredUserVars = [
|
|
83
|
+
"QUILT_CATALOG",
|
|
84
|
+
"QUILT_USER_BUCKET",
|
|
85
|
+
"BENCHLING_CLIENT_ID",
|
|
86
|
+
"BENCHLING_CLIENT_SECRET",
|
|
87
|
+
"BENCHLING_TENANT",
|
|
88
|
+
];
|
|
89
|
+
|
|
90
|
+
const missingVars = requiredUserVars.filter((varName) => !config[varName]);
|
|
91
|
+
|
|
92
|
+
if (missingVars.length > 0) {
|
|
93
|
+
console.error("Error: Missing required environment variables:");
|
|
94
|
+
missingVars.forEach((varName) => {
|
|
95
|
+
console.error(` - ${varName}`);
|
|
96
|
+
});
|
|
97
|
+
console.error("\nPlease set these variables in your .env file.");
|
|
98
|
+
console.error("See env.template for guidance.");
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Validate inferred values are present (should be available if catalog lookup succeeded)
|
|
103
|
+
const requiredInferredVars = [
|
|
104
|
+
"CDK_DEFAULT_ACCOUNT",
|
|
105
|
+
"CDK_DEFAULT_REGION",
|
|
106
|
+
"QUEUE_NAME",
|
|
107
|
+
"SQS_QUEUE_URL",
|
|
108
|
+
"QUILT_DATABASE",
|
|
109
|
+
];
|
|
110
|
+
|
|
111
|
+
const missingInferredVars = requiredInferredVars.filter((varName) => !config[varName]);
|
|
112
|
+
|
|
113
|
+
if (missingInferredVars.length > 0) {
|
|
114
|
+
console.error("Error: Could not infer required configuration:");
|
|
115
|
+
missingInferredVars.forEach((varName) => {
|
|
116
|
+
console.error(` - ${varName}`);
|
|
117
|
+
});
|
|
118
|
+
console.error("\nThese values should be automatically inferred from your Quilt catalog.");
|
|
119
|
+
console.error("Please ensure:");
|
|
120
|
+
console.error(" 1. QUILT_CATALOG is set correctly");
|
|
121
|
+
console.error(" 2. Your AWS credentials have CloudFormation read permissions");
|
|
122
|
+
console.error(" 3. The Quilt stack is deployed and accessible");
|
|
123
|
+
console.error("\nAlternatively, you can manually set these values in your .env file.");
|
|
124
|
+
process.exit(1);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Validate conditional requirements
|
|
128
|
+
if (config.ENABLE_WEBHOOK_VERIFICATION !== "false" && !config.BENCHLING_APP_DEFINITION_ID) {
|
|
129
|
+
console.error("Error: BENCHLING_APP_DEFINITION_ID is required when webhook verification is enabled.");
|
|
130
|
+
console.error("Either set BENCHLING_APP_DEFINITION_ID or set ENABLE_WEBHOOK_VERIFICATION=false");
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return config;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Main execution
|
|
139
|
+
*/
|
|
140
|
+
async function main() {
|
|
141
|
+
const config = await getConfig();
|
|
142
|
+
|
|
143
|
+
// Validate CDK bootstrap before proceeding
|
|
144
|
+
await checkCdkBootstrap(config.CDK_DEFAULT_ACCOUNT!, config.CDK_DEFAULT_REGION!);
|
|
145
|
+
|
|
146
|
+
const app = new cdk.App();
|
|
147
|
+
new BenchlingWebhookStack(app, "BenchlingWebhookStack", {
|
|
148
|
+
env: {
|
|
149
|
+
account: config.CDK_DEFAULT_ACCOUNT,
|
|
150
|
+
region: config.CDK_DEFAULT_REGION,
|
|
151
|
+
},
|
|
152
|
+
bucketName: config.QUILT_USER_BUCKET!, // User's data bucket
|
|
153
|
+
queueName: config.QUEUE_NAME!,
|
|
154
|
+
environment: "production",
|
|
155
|
+
prefix: config.PKG_PREFIX || "benchling",
|
|
156
|
+
benchlingClientId: config.BENCHLING_CLIENT_ID!,
|
|
157
|
+
benchlingClientSecret: config.BENCHLING_CLIENT_SECRET!,
|
|
158
|
+
benchlingTenant: config.BENCHLING_TENANT!,
|
|
159
|
+
quiltCatalog: config.QUILT_CATALOG!,
|
|
160
|
+
quiltDatabase: config.QUILT_DATABASE!,
|
|
161
|
+
webhookAllowList: config.WEBHOOK_ALLOW_LIST,
|
|
162
|
+
logLevel: config.LOG_LEVEL || "INFO",
|
|
163
|
+
// ECR repository configuration
|
|
164
|
+
createEcrRepository: config.CREATE_ECR_REPOSITORY === "true",
|
|
165
|
+
ecrRepositoryName: config.ECR_REPOSITORY_NAME || "quiltdata/benchling",
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
main().catch((error) => {
|
|
170
|
+
console.error("Fatal error during CDK synthesis:", error);
|
|
171
|
+
process.exit(1);
|
|
172
|
+
});
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Check CloudWatch logs for the deployed Benchling webhook ECS service
|
|
4
|
+
* Uses CloudFormation stack outputs to find the correct log group
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
require("dotenv/config");
|
|
8
|
+
const { execSync } = require("child_process");
|
|
9
|
+
|
|
10
|
+
const STACK_NAME = "BenchlingWebhookStack";
|
|
11
|
+
|
|
12
|
+
// Validate required environment variables
|
|
13
|
+
if (!process.env.CDK_DEFAULT_REGION) {
|
|
14
|
+
console.error("Error: CDK_DEFAULT_REGION is not set in .env file");
|
|
15
|
+
console.error("Please set CDK_DEFAULT_REGION in your .env file");
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const AWS_REGION = process.env.CDK_DEFAULT_REGION;
|
|
20
|
+
|
|
21
|
+
function getStackOutputs() {
|
|
22
|
+
try {
|
|
23
|
+
const output = execSync(
|
|
24
|
+
`aws cloudformation describe-stacks --stack-name ${STACK_NAME} --region ${AWS_REGION} --query 'Stacks[0].Outputs' --output json`,
|
|
25
|
+
{ encoding: "utf-8" },
|
|
26
|
+
);
|
|
27
|
+
return JSON.parse(output);
|
|
28
|
+
} catch (error) {
|
|
29
|
+
console.error(`Error: Could not get stack outputs for ${STACK_NAME}`);
|
|
30
|
+
console.error("Make sure the stack is deployed and AWS credentials are configured.");
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function getLogGroupFromOutputs(outputs, logType) {
|
|
36
|
+
let outputKey;
|
|
37
|
+
if (logType === "ecs") {
|
|
38
|
+
outputKey = "EcsLogGroup";
|
|
39
|
+
} else if (logType === "api") {
|
|
40
|
+
outputKey = "ApiGatewayLogGroup";
|
|
41
|
+
} else if (logType === "api-exec") {
|
|
42
|
+
outputKey = "ApiGatewayExecutionLogGroup";
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const logGroupOutput = outputs.find((o) => o.OutputKey === outputKey);
|
|
46
|
+
|
|
47
|
+
if (!logGroupOutput) {
|
|
48
|
+
console.error(`Error: Could not find ${outputKey} in stack outputs`);
|
|
49
|
+
console.error("Make sure the stack has been deployed with the latest changes.");
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return logGroupOutput.OutputValue;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function printStackInfo(outputs, logGroup, logType) {
|
|
57
|
+
console.log("=".repeat(80));
|
|
58
|
+
console.log("Benchling Webhook Stack Information");
|
|
59
|
+
console.log("=".repeat(80));
|
|
60
|
+
|
|
61
|
+
const clusterName = outputs.find((o) => o.OutputKey === "FargateServiceClusterNameCD3B109F");
|
|
62
|
+
const serviceName = outputs.find((o) => o.OutputKey === "FargateServiceServiceName24CFD869");
|
|
63
|
+
const webhookEndpoint = outputs.find((o) => o.OutputKey === "WebhookEndpoint");
|
|
64
|
+
const version = outputs.find((o) => o.OutputKey === "StackVersion");
|
|
65
|
+
const ecsLogGroup = outputs.find((o) => o.OutputKey === "EcsLogGroup");
|
|
66
|
+
const apiLogGroup = outputs.find((o) => o.OutputKey === "ApiGatewayLogGroup");
|
|
67
|
+
const apiExecLogGroup = outputs.find((o) => o.OutputKey === "ApiGatewayExecutionLogGroup");
|
|
68
|
+
const albDns = outputs.find((o) => o.OutputKey === "LoadBalancerDNS");
|
|
69
|
+
|
|
70
|
+
if (clusterName) console.log(`Cluster: ${clusterName.OutputValue}`);
|
|
71
|
+
if (serviceName) console.log(`Service: ${serviceName.OutputValue}`);
|
|
72
|
+
if (webhookEndpoint) console.log(`Endpoint: ${webhookEndpoint.OutputValue}`);
|
|
73
|
+
if (albDns) console.log(`ALB DNS: ${albDns.OutputValue}`);
|
|
74
|
+
if (version) console.log(`Version: ${version.OutputValue}`);
|
|
75
|
+
|
|
76
|
+
console.log("");
|
|
77
|
+
console.log("Log Groups:");
|
|
78
|
+
if (ecsLogGroup) console.log(` ECS: ${ecsLogGroup.OutputValue}${logType === "ecs" ? " (viewing)" : ""}`);
|
|
79
|
+
if (apiLogGroup) console.log(` API Access: ${apiLogGroup.OutputValue}${logType === "api" ? " (viewing)" : ""}`);
|
|
80
|
+
if (apiExecLogGroup) console.log(` API Exec: ${apiExecLogGroup.OutputValue}${logType === "api-exec" ? " (viewing)" : ""}`);
|
|
81
|
+
|
|
82
|
+
console.log("=".repeat(80));
|
|
83
|
+
console.log("");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function main() {
|
|
87
|
+
const args = process.argv.slice(2);
|
|
88
|
+
const logType = args.find((arg) => arg.startsWith("--type="))?.split("=")[1] || "all";
|
|
89
|
+
const filterPattern = args.find((arg) => arg.startsWith("--filter="))?.split("=")[1];
|
|
90
|
+
const since = args.find((arg) => arg.startsWith("--since="))?.split("=")[1] || "5m";
|
|
91
|
+
const follow = args.includes("--follow") || args.includes("-f");
|
|
92
|
+
const tail = args.find((arg) => arg.startsWith("--tail="))?.split("=")[1] || "100";
|
|
93
|
+
|
|
94
|
+
// Validate log type
|
|
95
|
+
if (!["ecs", "api", "api-exec", "all"].includes(logType)) {
|
|
96
|
+
console.error("Error: --type must be 'ecs', 'api', 'api-exec', or 'all'");
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Get stack outputs
|
|
101
|
+
const outputs = getStackOutputs();
|
|
102
|
+
|
|
103
|
+
// Handle 'all' type - show all three log groups
|
|
104
|
+
if (logType === "all") {
|
|
105
|
+
printStackInfo(outputs, null, "all");
|
|
106
|
+
console.log("Showing logs from all sources (most recent first):\n");
|
|
107
|
+
|
|
108
|
+
const logGroupDefs = [
|
|
109
|
+
{ type: "ECS", group: outputs.find((o) => o.OutputKey === "EcsLogGroup")?.OutputValue },
|
|
110
|
+
{ type: "API-Access", group: outputs.find((o) => o.OutputKey === "ApiGatewayLogGroup")?.OutputValue },
|
|
111
|
+
{ type: "API-Exec", group: outputs.find((o) => o.OutputKey === "ApiGatewayExecutionLogGroup")?.OutputValue },
|
|
112
|
+
];
|
|
113
|
+
|
|
114
|
+
// Warn about missing log groups
|
|
115
|
+
const missingGroups = logGroupDefs.filter(lg => !lg.group);
|
|
116
|
+
if (missingGroups.length > 0) {
|
|
117
|
+
console.log("⚠️ WARNING: Some log groups are not available in stack outputs:");
|
|
118
|
+
missingGroups.forEach(({ type }) => {
|
|
119
|
+
console.log(` - ${type}: Stack output not found (may need to redeploy stack)`);
|
|
120
|
+
});
|
|
121
|
+
console.log("");
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const logGroups = logGroupDefs.filter(lg => lg.group);
|
|
125
|
+
|
|
126
|
+
for (const { type, group } of logGroups) {
|
|
127
|
+
console.log(`\n${"=".repeat(80)}`);
|
|
128
|
+
console.log(`${type}: ${group}`);
|
|
129
|
+
console.log("=".repeat(80));
|
|
130
|
+
|
|
131
|
+
let command = `aws logs tail "${group}"`;
|
|
132
|
+
command += ` --region ${AWS_REGION}`;
|
|
133
|
+
command += ` --since ${since}`;
|
|
134
|
+
command += ` --format short`;
|
|
135
|
+
if (filterPattern) {
|
|
136
|
+
command += ` --filter-pattern "${filterPattern}"`;
|
|
137
|
+
}
|
|
138
|
+
command += ` 2>&1 | tail -${tail}`;
|
|
139
|
+
|
|
140
|
+
try {
|
|
141
|
+
const output = execSync(command, { encoding: "utf-8", shell: "/bin/bash" });
|
|
142
|
+
if (output.trim()) {
|
|
143
|
+
console.log(output);
|
|
144
|
+
} else {
|
|
145
|
+
console.log(`(No logs in the last ${since})`);
|
|
146
|
+
}
|
|
147
|
+
} catch (error) {
|
|
148
|
+
console.log(`Error reading ${type} logs: ${error.message}`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const logGroup = getLogGroupFromOutputs(outputs, logType);
|
|
155
|
+
printStackInfo(outputs, logGroup, logType);
|
|
156
|
+
|
|
157
|
+
// Build AWS logs command
|
|
158
|
+
let command = `aws logs tail ${logGroup}`;
|
|
159
|
+
command += ` --region ${AWS_REGION}`;
|
|
160
|
+
command += ` --since ${since}`;
|
|
161
|
+
command += ` --format short`;
|
|
162
|
+
|
|
163
|
+
if (filterPattern) {
|
|
164
|
+
command += ` --filter-pattern "${filterPattern}"`;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (follow) {
|
|
168
|
+
command += " --follow";
|
|
169
|
+
console.log("Following logs (Press Ctrl+C to stop)...\n");
|
|
170
|
+
} else {
|
|
171
|
+
command += ` | tail -${tail}`;
|
|
172
|
+
console.log(`Showing last ${tail} log entries from the past ${since}...\n`);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Execute logs command
|
|
176
|
+
try {
|
|
177
|
+
execSync(command, { stdio: "inherit" });
|
|
178
|
+
} catch (error) {
|
|
179
|
+
if (error.status !== 130) {
|
|
180
|
+
// Ignore Ctrl+C exit (status 130)
|
|
181
|
+
console.error("\nError fetching logs. Make sure:");
|
|
182
|
+
console.error("1. The stack is deployed");
|
|
183
|
+
console.error("2. AWS CLI is configured with proper credentials");
|
|
184
|
+
console.error("3. You have CloudWatch Logs read permissions");
|
|
185
|
+
process.exit(1);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function printHelp() {
|
|
191
|
+
console.log("Usage: npm run logs [options]");
|
|
192
|
+
console.log("");
|
|
193
|
+
console.log("Options:");
|
|
194
|
+
console.log(" --type=TYPE Log group to view (default: all)");
|
|
195
|
+
console.log(" all = All logs (ECS, API Access, API Execution)");
|
|
196
|
+
console.log(" ecs = ECS container logs (application logs)");
|
|
197
|
+
console.log(" api = API Gateway access logs (requests/responses)");
|
|
198
|
+
console.log(" api-exec = API Gateway execution logs (detailed debugging)");
|
|
199
|
+
console.log(" --since=TIME Time period to fetch logs (default: 5m)");
|
|
200
|
+
console.log(" Examples: 1h, 30m, 2d, 5m");
|
|
201
|
+
console.log(" --filter=PATTERN Filter logs by pattern");
|
|
202
|
+
console.log(" Examples: --filter=ERROR, --filter=canvas, --filter=500");
|
|
203
|
+
console.log(" --follow, -f Follow log output (like tail -f, not available with --type=all)");
|
|
204
|
+
console.log(" --tail=N Show last N lines (default: 100, only without --follow)");
|
|
205
|
+
console.log(" --help, -h Show this help message");
|
|
206
|
+
console.log("");
|
|
207
|
+
console.log("Examples:");
|
|
208
|
+
console.log(" npm run logs # View all logs from past 5 min");
|
|
209
|
+
console.log(" npm run logs -- --type=ecs # View only ECS logs");
|
|
210
|
+
console.log(" npm run logs -- --type=api-exec # View API Gateway execution logs");
|
|
211
|
+
console.log(" npm run logs -- --since=1h # Last hour of all logs");
|
|
212
|
+
console.log(" npm run logs -- --filter=ERROR # Filter for errors in all logs");
|
|
213
|
+
console.log(" npm run logs -- --type=api-exec --filter=500 # API Gateway execution errors");
|
|
214
|
+
console.log(" npm run logs -- --type=ecs --follow # Follow ECS logs");
|
|
215
|
+
console.log(" npm run logs -- --type=api-exec --since=10m # Last 10 min of execution logs");
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (require.main === module) {
|
|
219
|
+
const args = process.argv.slice(2);
|
|
220
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
221
|
+
printHelp();
|
|
222
|
+
process.exit(0);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
try {
|
|
226
|
+
main();
|
|
227
|
+
} catch (error) {
|
|
228
|
+
console.error("Error:", error.message);
|
|
229
|
+
process.exit(1);
|
|
230
|
+
}
|
|
231
|
+
}
|
package/bin/cli-auth.sh
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Ensure environment variables are set
|
|
4
|
+
if [[ -z "$BENCHLING_CLIENT_ID" || -z "$BENCHLING_TENANT" || -z "$BENCHLING_CLIENT_SECRET" ]]; then
|
|
5
|
+
echo "Error: Required environment variables are not set. Please source .env first."
|
|
6
|
+
exit 1
|
|
7
|
+
fi
|
|
8
|
+
|
|
9
|
+
# Debugging: Print the extracted values
|
|
10
|
+
echo "BENCHLING_CLIENT_ID: $BENCHLING_CLIENT_ID"
|
|
11
|
+
echo "BENCHLING_TENANT: $BENCHLING_TENANT"
|
|
12
|
+
|
|
13
|
+
API_ROOT="https://${BENCHLING_TENANT}.benchling.com/api/v2"
|
|
14
|
+
|
|
15
|
+
# Function to get OAuth Token
|
|
16
|
+
get_token() {
|
|
17
|
+
curl -s -X POST "$API_ROOT/token" \
|
|
18
|
+
-H "Content-Type: application/x-www-form-urlencoded" \
|
|
19
|
+
-d "client_id=${BENCHLING_CLIENT_ID}" \
|
|
20
|
+
-d "client_secret=${BENCHLING_CLIENT_SECRET}" \
|
|
21
|
+
-d "grant_type=client_credentials" | jq -r '.access_token'
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
# Generic function to make API requests
|
|
25
|
+
api_request() {
|
|
26
|
+
local method=$1
|
|
27
|
+
local endpoint=$2
|
|
28
|
+
local data=$3
|
|
29
|
+
|
|
30
|
+
curl -v -X "$method" "$API_ROOT/$endpoint" \
|
|
31
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
32
|
+
-H "Content-Type: application/json" \
|
|
33
|
+
${data:+--data "$data"}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
# Get OAuth Token
|
|
37
|
+
TOKEN=$(get_token)
|
|
38
|
+
|
|
39
|
+
# Export TOKEN globally
|
|
40
|
+
export TOKEN
|
|
41
|
+
|
|
42
|
+
# Debugging: Print the token
|
|
43
|
+
if [[ -z "$TOKEN" || "$TOKEN" == "null" ]]; then
|
|
44
|
+
echo "Error: Failed to retrieve access token."
|
|
45
|
+
exit 1
|
|
46
|
+
fi
|
|
47
|
+
echo "TOKEN: $TOKEN"
|
|
48
|
+
|
|
49
|
+
# Check if CANVAS_ID is provided as an argument
|
|
50
|
+
if [[ -n "$1" ]]; then
|
|
51
|
+
CANVAS_ID="$1"
|
|
52
|
+
echo "Fetching canvas with ID: $CANVAS_ID"
|
|
53
|
+
echo "=== $CANVAS_ID ==="
|
|
54
|
+
api_request "GET" "app-canvases/${CANVAS_ID}"
|
|
55
|
+
echo "=== $CANVAS_ID ==="
|
|
56
|
+
|
|
57
|
+
echo "Updating canvas with ID: $CANVAS_ID"
|
|
58
|
+
api_request "PATCH" "app-canvases/${CANVAS_ID}" '{
|
|
59
|
+
"blocks": [
|
|
60
|
+
{
|
|
61
|
+
"enabled": true,
|
|
62
|
+
"id": "user_defined_id",
|
|
63
|
+
"text": "Click me to submit",
|
|
64
|
+
"type": "BUTTON"
|
|
65
|
+
}
|
|
66
|
+
],
|
|
67
|
+
"enabled": true,
|
|
68
|
+
"featureId": "quilt_integration"
|
|
69
|
+
}'
|
|
70
|
+
|
|
71
|
+
else
|
|
72
|
+
echo "No canvas ID provided. Fetching apps instead."
|
|
73
|
+
api_request "GET" "apps"
|
|
74
|
+
fi
|