pdk_test/services/flex/
api.rs1use crate::config::Config;
6use crate::port::Port;
7use crate::services::flex::gcl::ToGcl;
8use crate::services::flex::policy::PolicyConfig;
9use convert_case::{Case, Casing};
10use indoc::formatdoc;
11use std::ops::Add;
12
13#[derive(Debug, Clone)]
15pub struct ApiConfig {
16 pub(super) name: String,
17 upstream: String,
18 pub(super) port: Port,
19 path: String,
20 policies: Vec<PolicyConfig>,
21}
22
23impl ApiConfig {
24 fn new() -> Self {
25 Self {
26 name: "ingress-http".to_string(),
27 upstream: "http://backend:80".to_string(),
28 port: 8081,
29 path: "/anything/echo/".to_string(),
30 policies: vec![],
31 }
32 }
33
34 pub fn builder() -> ApiConfigBuilder {
36 ApiConfigBuilder::new()
37 }
38}
39
40#[derive(Debug, Clone)]
42pub struct ApiConfigBuilder {
43 config: ApiConfig,
44}
45
46impl ApiConfigBuilder {
47 fn new() -> Self {
48 Self {
49 config: ApiConfig::new(),
50 }
51 }
52
53 pub fn name<T: Into<String>>(self, name: T) -> Self {
55 Self {
56 config: ApiConfig {
57 name: name.into(),
58 ..self.config
59 },
60 }
61 }
62
63 pub fn upstream(self, upstream: &dyn Config) -> Self {
65 Self {
66 config: ApiConfig {
67 upstream: format!(
68 "{}://{}:{}",
69 upstream.schema(),
70 upstream.hostname(),
71 upstream.port()
72 ),
73 ..self.config
74 },
75 }
76 }
77
78 pub fn port(self, port: Port) -> Self {
80 Self {
81 config: ApiConfig {
82 port,
83 ..self.config
84 },
85 }
86 }
87
88 pub fn path<T: Into<String>>(self, path: T) -> Self {
90 Self {
91 config: ApiConfig {
92 path: path.into(),
93 ..self.config
94 },
95 }
96 }
97
98 pub fn policies<T>(self, policies: T) -> Self
100 where
101 T: IntoIterator<Item = PolicyConfig>,
102 {
103 Self {
104 config: ApiConfig {
105 policies: policies.into_iter().collect(),
106 ..self.config
107 },
108 }
109 }
110
111 pub fn build(self) -> ApiConfig {
113 self.config
114 }
115}
116
117const API_POLICY_SECTION_GCL: &str = r#" policies:"#;
118
119impl ToGcl for ApiConfig {
120 fn to_gcl(&self) -> String {
121 let name = self.name.to_case(Case::Kebab);
122 let port = self.port;
123 let upstream = self.upstream.as_str();
124 let path = self.path.as_str();
125
126 let mut result = formatdoc!(
127 "
128 # Copyright (c) 2025, Salesforce, Inc.,
129 # All rights reserved.
130 # For full license text, see the LICENSE.txt file
131 ---
132 apiVersion: gateway.mulesoft.com/v1alpha1
133 kind: ApiInstance
134 metadata:
135 name: {name}
136 spec:
137 address: http://0.0.0.0:{port}
138 services:
139 upstream:
140 address: {upstream}
141 routes:
142 - config:
143 destinationPath: {path}
144 "
145 );
146
147 if !self.policies.is_empty() {
148 result = result.add(API_POLICY_SECTION_GCL);
149 }
150
151 for policy_config in self.policies.iter() {
152 result = result.add(&policy_config.to_gcl());
153 }
154
155 result
156 }
157
158 fn name(&self) -> &str {
159 &self.name
160 }
161}
162
163#[cfg(test)]
164mod tests {
165 use crate::services::flex::gcl::ToGcl;
166 use crate::services::flex::{ApiConfig, PolicyConfig};
167 use crate::services::httpmock::HttpMockConfig;
168 use indoc::formatdoc;
169
170 #[test]
171 fn generate_api_gcl() {
172 let upstream_config = HttpMockConfig::builder().hostname("mock").port(80).build();
173
174 let policy_config = PolicyConfig::builder()
175 .name("simple-oauth-2-validation-v1-0-impl")
176 .configuration(serde_json::json!({
177 "authorization": "whatever",
178 "oauthService": "http://mock:80/auth",
179 }))
180 .build();
181
182 let api_config = ApiConfig::builder()
183 .upstream(&upstream_config)
184 .policies([policy_config])
185 .build();
186
187 let expected = formatdoc!(
188 r#"
189 # Copyright (c) 2025, Salesforce, Inc.,
190 # All rights reserved.
191 # For full license text, see the LICENSE.txt file
192 ---
193 apiVersion: gateway.mulesoft.com/v1alpha1
194 kind: ApiInstance
195 metadata:
196 name: ingress-http
197 spec:
198 address: http://0.0.0.0:8081
199 services:
200 upstream:
201 address: http://mock:80
202 routes:
203 - config:
204 destinationPath: /anything/echo/
205 policies:
206 - policyRef:
207 name: simple-oauth-2-validation-v1-0-impl
208 namespace: default
209 config: {{"authorization":"whatever","oauthService":"http://mock:80/auth"}}"#
210 );
211
212 assert_eq!(api_config.to_gcl(), expected)
213 }
214}