Skip to content

Commit f697f94

Browse files
thomasballingerConvex, Inc.
authored andcommitted
Add WorkOS team billing status to CLI (#43343)
GitOrigin-RevId: ada1da78852ec5fcfac48c9157bacba6f4a556d8
1 parent 6d6d64b commit f697f94

File tree

7 files changed

+638
-28
lines changed

7 files changed

+638
-28
lines changed

crates/workos_client/src/lib.rs

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,13 +79,22 @@ fn format_workos_error(operation: &str, status: http::StatusCode, response_body:
7979
)
8080
}
8181

82+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
83+
#[serde(rename_all = "PascalCase")]
84+
pub enum WorkOSProductionState {
85+
Active,
86+
Inactive,
87+
}
88+
8289
#[derive(Debug, Deserialize, Serialize)]
8390
pub struct WorkOSTeamResponse {
8491
/// always "team"
8592
pub object: String,
8693
/// like "team_01K58C005DSAQCZSX84FFWMT5G"
8794
pub id: String,
8895
pub name: String,
96+
#[serde(skip_serializing_if = "Option::is_none")]
97+
pub production_state: Option<WorkOSProductionState>,
8998
pub created_at: String,
9099
pub updated_at: String,
91100
}
@@ -239,11 +248,17 @@ pub trait WorkOSPlatformClient: Send + Sync {
239248
admin_email: &str,
240249
team_name: &str,
241250
) -> anyhow::Result<WorkOSTeamResponse>;
251+
async fn get_team(&self, workos_team_id: &str) -> anyhow::Result<WorkOSTeamResponse>;
242252
async fn create_environment(
243253
&self,
244254
workos_team_id: &str,
245255
environment_name: &str,
246256
) -> anyhow::Result<WorkOSEnvironmentResponse>;
257+
async fn get_environment(
258+
&self,
259+
workos_team_id: &str,
260+
environment_id: &str,
261+
) -> anyhow::Result<WorkOSEnvironmentResponse>;
247262
async fn create_api_key(
248263
&self,
249264
workos_team_id: &str,
@@ -584,6 +599,10 @@ where
584599
.await
585600
}
586601

602+
async fn get_team(&self, workos_team_id: &str) -> anyhow::Result<WorkOSTeamResponse> {
603+
get_workos_team(&self.platform_api_key, workos_team_id, &*self.http_client).await
604+
}
605+
587606
async fn create_environment(
588607
&self,
589608
workos_team_id: &str,
@@ -598,6 +617,20 @@ where
598617
.await
599618
}
600619

620+
async fn get_environment(
621+
&self,
622+
workos_team_id: &str,
623+
environment_id: &str,
624+
) -> anyhow::Result<WorkOSEnvironmentResponse> {
625+
get_workos_environment(
626+
&self.platform_api_key,
627+
workos_team_id,
628+
environment_id,
629+
&*self.http_client,
630+
)
631+
.await
632+
}
633+
601634
async fn create_api_key(
602635
&self,
603636
workos_team_id: &str,
@@ -640,6 +673,18 @@ impl WorkOSPlatformClient for MockWorkOSPlatformClient {
640673
object: "team".to_string(),
641674
id: "team_mock123".to_string(),
642675
name: team_name.to_string(),
676+
production_state: Some(WorkOSProductionState::Active),
677+
created_at: "2024-01-01T00:00:00.000Z".to_string(),
678+
updated_at: "2024-01-01T00:00:00.000Z".to_string(),
679+
})
680+
}
681+
682+
async fn get_team(&self, workos_team_id: &str) -> anyhow::Result<WorkOSTeamResponse> {
683+
Ok(WorkOSTeamResponse {
684+
object: "team".to_string(),
685+
id: workos_team_id.to_string(),
686+
name: "Mock Team".to_string(),
687+
production_state: Some(WorkOSProductionState::Active),
643688
created_at: "2024-01-01T00:00:00.000Z".to_string(),
644689
updated_at: "2024-01-01T00:00:00.000Z".to_string(),
645690
})
@@ -658,6 +703,19 @@ impl WorkOSPlatformClient for MockWorkOSPlatformClient {
658703
})
659704
}
660705

706+
async fn get_environment(
707+
&self,
708+
_workos_team_id: &str,
709+
environment_id: &str,
710+
) -> anyhow::Result<WorkOSEnvironmentResponse> {
711+
Ok(WorkOSEnvironmentResponse {
712+
object: "environment".to_string(),
713+
id: environment_id.to_string(),
714+
name: "Mock Environment".to_string(),
715+
client_id: "client_mock123".to_string(),
716+
})
717+
}
718+
661719
async fn create_api_key(
662720
&self,
663721
_workos_team_id: &str,
@@ -920,6 +978,51 @@ where
920978
Ok(team)
921979
}
922980

981+
pub async fn get_workos_team<F, E>(
982+
api_key: &str,
983+
workos_team_id: &str,
984+
http_client: &(impl Fn(HttpRequest) -> F + 'static + ?Sized),
985+
) -> anyhow::Result<WorkOSTeamResponse>
986+
where
987+
F: Future<Output = Result<HttpResponse, E>>,
988+
E: std::error::Error + 'static + Send + Sync,
989+
{
990+
let url = format!("https://api.workos.com/platform/teams/{workos_team_id}");
991+
992+
let request = http::Request::builder()
993+
.uri(&url)
994+
.method(http::Method::GET)
995+
.header(http::header::AUTHORIZATION, format!("Bearer {api_key}"))
996+
.header(http::header::ACCEPT, APPLICATION_JSON)
997+
.body(vec![])?;
998+
999+
let response = timeout(WORKOS_API_TIMEOUT, http_client(request))
1000+
.await
1001+
.map_err(|_| {
1002+
anyhow::anyhow!(
1003+
"WorkOS API call timed out after {}s",
1004+
WORKOS_API_TIMEOUT.as_secs()
1005+
)
1006+
})?
1007+
.map_err(|e| anyhow::anyhow!("Could not get WorkOS team: {}", e))?;
1008+
1009+
if !response.status().is_success() {
1010+
let status = response.status();
1011+
let response_body = response.into_body();
1012+
anyhow::bail!(format_workos_error("get team", status, &response_body));
1013+
}
1014+
1015+
let response_body = response.into_body();
1016+
let team: WorkOSTeamResponse = serde_json::from_slice(&response_body).with_context(|| {
1017+
format!(
1018+
"Invalid WorkOS team response: {}",
1019+
String::from_utf8_lossy(&response_body)
1020+
)
1021+
})?;
1022+
1023+
Ok(team)
1024+
}
1025+
9231026
pub async fn create_workos_environment<F, E>(
9241027
api_key: &str,
9251028
workos_team_id: &str,
@@ -981,6 +1084,59 @@ where
9811084
Ok(environment)
9821085
}
9831086

1087+
pub async fn get_workos_environment<F, E>(
1088+
api_key: &str,
1089+
workos_team_id: &str,
1090+
environment_id: &str,
1091+
http_client: &(impl Fn(HttpRequest) -> F + 'static + ?Sized),
1092+
) -> anyhow::Result<WorkOSEnvironmentResponse>
1093+
where
1094+
F: Future<Output = Result<HttpResponse, E>>,
1095+
E: std::error::Error + 'static + Send + Sync,
1096+
{
1097+
let url = format!(
1098+
"https://api.workos.com/platform/teams/{workos_team_id}/environments/{environment_id}"
1099+
);
1100+
1101+
let request = http::Request::builder()
1102+
.uri(&url)
1103+
.method(http::Method::GET)
1104+
.header(http::header::AUTHORIZATION, format!("Bearer {api_key}"))
1105+
.header(http::header::ACCEPT, APPLICATION_JSON)
1106+
.body(vec![])?;
1107+
1108+
let response = timeout(WORKOS_API_TIMEOUT, http_client(request))
1109+
.await
1110+
.map_err(|_| {
1111+
anyhow::anyhow!(
1112+
"WorkOS API call timed out after {}s",
1113+
WORKOS_API_TIMEOUT.as_secs()
1114+
)
1115+
})?
1116+
.map_err(|e| anyhow::anyhow!("Could not get WorkOS environment: {}", e))?;
1117+
1118+
if !response.status().is_success() {
1119+
let status = response.status();
1120+
let response_body = response.into_body();
1121+
anyhow::bail!(format_workos_error(
1122+
"get environment",
1123+
status,
1124+
&response_body
1125+
));
1126+
}
1127+
1128+
let response_body = response.into_body();
1129+
let environment: WorkOSEnvironmentResponse = serde_json::from_slice(&response_body)
1130+
.with_context(|| {
1131+
format!(
1132+
"Invalid WorkOS environment response: {}",
1133+
String::from_utf8_lossy(&response_body)
1134+
)
1135+
})?;
1136+
1137+
Ok(environment)
1138+
}
1139+
9841140
pub async fn create_workos_api_key<F, E>(
9851141
api_key: &str,
9861142
workos_team_id: &str,

npm-packages/convex/management-openapi.json

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,61 @@
133133
}
134134
}
135135
},
136+
"/deployments/{deployment_name}/workos_environment_health": {
137+
"get": {
138+
"description": "Check if the WorkOS environment associated with this deployment is still accessible",
139+
"operationId": "get_workos_environment_health",
140+
"parameters": [
141+
{
142+
"name": "deployment_name",
143+
"in": "path",
144+
"description": "Deployment name",
145+
"required": true,
146+
"schema": {
147+
"type": "string"
148+
}
149+
}
150+
],
151+
"responses": {
152+
"200": {
153+
"description": "",
154+
"content": {
155+
"application/json": {
156+
"schema": {
157+
"$ref": "#/components/schemas/WorkOSEnvironmentHealthResponse"
158+
}
159+
}
160+
}
161+
}
162+
}
163+
}
164+
},
165+
"/teams/{team_id}/workos_team_health": {
166+
"get": {
167+
"description": "Check if the WorkOS team associated with this Convex team is still accessible",
168+
"operationId": "get_workos_team_health",
169+
"parameters": [
170+
{
171+
"name": "team_id",
172+
"in": "path",
173+
"description": "Convex team ID",
174+
"required": true
175+
}
176+
],
177+
"responses": {
178+
"200": {
179+
"description": "",
180+
"content": {
181+
"application/json": {
182+
"schema": {
183+
"$ref": "#/components/schemas/WorkOSTeamHealthResponse"
184+
}
185+
}
186+
}
187+
}
188+
}
189+
}
190+
},
136191
"/workos/has_associated_workos_team": {
137192
"post": {
138193
"description": "Check if a deployment has an associated WorkOS team",
@@ -482,6 +537,51 @@
482537
},
483538
"TeamSlug": {
484539
"type": "string"
540+
},
541+
"WorkOSEnvironmentHealthResponse": {
542+
"type": "object",
543+
"required": [
544+
"id",
545+
"name",
546+
"clientId"
547+
],
548+
"properties": {
549+
"clientId": {
550+
"type": "string"
551+
},
552+
"id": {
553+
"type": "string"
554+
},
555+
"name": {
556+
"type": "string"
557+
}
558+
}
559+
},
560+
"WorkOSTeamHealthResponse": {
561+
"type": "object",
562+
"required": [
563+
"id",
564+
"name",
565+
"teamStatus"
566+
],
567+
"properties": {
568+
"id": {
569+
"type": "string"
570+
},
571+
"name": {
572+
"type": "string"
573+
},
574+
"teamStatus": {
575+
"$ref": "#/components/schemas/WorkOSTeamStatus"
576+
}
577+
}
578+
},
579+
"WorkOSTeamStatus": {
580+
"type": "string",
581+
"enum": [
582+
"active",
583+
"inactive"
584+
]
485585
}
486586
}
487587
}

0 commit comments

Comments
 (0)