Coverage for cli / commands / webhooks_cmd.py: 100.00%

101 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-10-28 16:19 +0000

1"""Webhook commands.""" 

2 

3import sys 

4from typing import Any 

5 

6import click 

7 

8from ..config import GCOConfig 

9from ..output import confirm, get_output_formatter 

10 

11pass_config = click.make_pass_decorator(GCOConfig, ensure=True) 

12 

13 

14@click.group() 

15@pass_config 

16def webhooks(config: Any) -> None: 

17 """Manage webhooks for job event notifications. 

18 

19 Webhooks receive HTTP POST notifications when job events occur 

20 (job.started, job.completed, job.failed). 

21 """ 

22 

23 

24@webhooks.command("list") 

25@click.option("--namespace", "-n", help="Filter by namespace") 

26@click.option("--region", "-r", help="Region to query (any region works)") 

27@pass_config 

28def webhooks_list(config: Any, namespace: Any, region: Any) -> None: 

29 """List all registered webhooks. 

30 

31 Examples: 

32 gco webhooks list 

33 gco webhooks list --namespace gco-jobs 

34 """ 

35 formatter = get_output_formatter(config) 

36 

37 try: 

38 from ..aws_client import get_aws_client 

39 

40 aws_client = get_aws_client(config) 

41 

42 query_region = region or (config.default_region if config.use_regional_api else None) 

43 params = {} 

44 if namespace: 

45 params["namespace"] = namespace 

46 

47 result = aws_client.call_api( 

48 method="GET", 

49 path="/api/v1/webhooks", 

50 region=query_region, 

51 params=params, 

52 ) 

53 

54 if config.output_format == "table": 

55 webhooks_data = result.get("webhooks", []) 

56 if not webhooks_data: 

57 formatter.print_info("No webhooks found") 

58 return 

59 

60 print(f"\n Webhooks ({result.get('count', 0)} total)") 

61 print(" " + "-" * 80) 

62 print( 

63 " ID URL EVENTS NAMESPACE" 

64 ) 

65 print(" " + "-" * 80) 

66 for w in webhooks_data: 

67 wid = w.get("id", "")[:8] 

68 url = w.get("url", "")[:40] 

69 events = ",".join(w.get("events", []))[:18] 

70 ns = (w.get("namespace") or "all")[:12] 

71 print(f" {wid:<9} {url:<42} {events:<19} {ns}") 

72 else: 

73 formatter.print(result) 

74 

75 except Exception as e: 

76 formatter.print_error(f"Failed to list webhooks: {e}") 

77 sys.exit(1) 

78 

79 

80@webhooks.command("get") 

81@click.argument("webhook_id") 

82@click.option("--region", "-r", help="Region to query (any region works)") 

83@pass_config 

84def webhooks_get(config: Any, webhook_id: Any, region: Any) -> None: 

85 """Get a single webhook by id. 

86 

87 The webhooks API exposes list/create/delete but no fetch-by-id endpoint, so 

88 this lists the region's webhooks and returns the one whose id matches. 

89 

90 Examples: 

91 gco webhooks get abc12345 

92 gco webhooks get abc12345 -r us-east-1 

93 """ 

94 formatter = get_output_formatter(config) 

95 

96 try: 

97 from ..aws_client import get_aws_client 

98 

99 aws_client = get_aws_client(config) 

100 

101 query_region = region or (config.default_region if config.use_regional_api else None) 

102 result = aws_client.call_api( 

103 method="GET", 

104 path="/api/v1/webhooks", 

105 region=query_region, 

106 ) 

107 match = next( 

108 (w for w in result.get("webhooks", []) if w.get("id") == webhook_id), 

109 None, 

110 ) 

111 if match is None: 

112 formatter.print_error(f"Webhook '{webhook_id}' not found") 

113 sys.exit(1) 

114 formatter.print(match) 

115 

116 except SystemExit: 

117 raise 

118 except Exception as e: 

119 formatter.print_error(f"Failed to get webhook: {e}") 

120 sys.exit(1) 

121 

122 

123@webhooks.command("create") 

124@click.option("--url", "-u", required=True, help="Webhook URL") 

125@click.option( 

126 "--event", 

127 "-e", 

128 multiple=True, 

129 required=True, 

130 type=click.Choice(["job.started", "job.completed", "job.failed"]), 

131 help="Events to subscribe to", 

132) 

133@click.option("--namespace", "-n", help="Filter events by namespace") 

134@click.option("--secret", "-s", help="Secret for HMAC signature verification") 

135@click.option("--region", "-r", help="Region to use (any region works)") 

136@pass_config 

137def webhooks_create( 

138 config: Any, url: Any, event: Any, namespace: Any, secret: Any, region: Any 

139) -> None: 

140 """Register a new webhook for job events. 

141 

142 Examples: 

143 gco webhooks create --url https://example.com/webhook -e job.completed -e job.failed 

144 gco webhooks create -u https://slack.com/webhook -e job.failed -n gco-jobs 

145 """ 

146 formatter = get_output_formatter(config) 

147 

148 try: 

149 from ..aws_client import get_aws_client 

150 

151 aws_client = get_aws_client(config) 

152 

153 query_region = region or (config.default_region if config.use_regional_api else None) 

154 result = aws_client.call_api( 

155 method="POST", 

156 path="/api/v1/webhooks", 

157 region=query_region, 

158 body={ 

159 "url": url, 

160 "events": list(event), 

161 "namespace": namespace, 

162 "secret": secret, 

163 }, 

164 ) 

165 

166 formatter.print_success("Webhook registered successfully") 

167 formatter.print(result) 

168 

169 except Exception as e: 

170 formatter.print_error(f"Failed to create webhook: {e}") 

171 sys.exit(1) 

172 

173 

174@webhooks.command("delete") 

175@click.argument("webhook_id") 

176@click.option("--region", "-r", help="Region to use (any region works)") 

177@click.option("--yes", "-y", is_flag=True, help="Skip confirmation") 

178@pass_config 

179def webhooks_delete(config: Any, webhook_id: Any, region: Any, yes: Any) -> None: 

180 """Delete a webhook. 

181 

182 Examples: 

183 gco webhooks delete abc123 

184 gco webhooks delete abc123 -y 

185 """ 

186 formatter = get_output_formatter(config) 

187 

188 if not yes: 

189 confirm(f"Delete webhook '{webhook_id}'?", abort=True) 

190 

191 try: 

192 from ..aws_client import get_aws_client 

193 

194 aws_client = get_aws_client(config) 

195 

196 query_region = region or (config.default_region if config.use_regional_api else None) 

197 result = aws_client.call_api( 

198 method="DELETE", 

199 path=f"/api/v1/webhooks/{webhook_id}", 

200 region=query_region, 

201 ) 

202 

203 formatter.print_success(f"Webhook '{webhook_id}' deleted") 

204 formatter.print(result) 

205 

206 except Exception as e: 

207 formatter.print_error(f"Failed to delete webhook: {e}") 

208 sys.exit(1)