Skip to content

Commit e834ea5

Browse files
committed
Initial implementation, prepare, gen.
0 parents  commit e834ea5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+51395
-0
lines changed

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
capi/*.json
2+
capi/*.yml
3+
.DS_Store
4+
openapitools.json
5+
clients/

Makefile

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
.PHONY: deps deps-openapi deps-spruce deps-jq deps-java help gen-go-client
2+
3+
# Default target
4+
.DEFAULT_GOAL := help
5+
6+
# Variables
7+
SHELL := /bin/bash
8+
UNAME_S := $(shell uname -s)
9+
OPENAPI_GEN_VERSION := 7.2.0
10+
CAPI_VERSION ?= 3.181.0
11+
12+
help: ## Display this help screen
13+
@grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
14+
15+
deps: deps-java deps-openapi deps-spruce deps-jq ## Install all dependencies
16+
17+
deps-java: ## Install Java Runtime Environment
18+
@echo "Checking/Installing Java..."
19+
@if [ "$(UNAME_S)" = "Darwin" ]; then \
20+
if ! command -v java &> /dev/null; then \
21+
if ! command -v brew &> /dev/null; then \
22+
echo "Homebrew is not installed. Please install it first: https://brew.sh/"; \
23+
exit 1; \
24+
fi; \
25+
brew install openjdk@17; \
26+
sudo ln -sfn $(brew --prefix)/opt/openjdk@17/libexec/openjdk.jdk /Library/Java/JavaVirtualMachines/openjdk-17.jdk; \
27+
fi \
28+
else \
29+
if ! command -v java &> /dev/null; then \
30+
if command -v apt-get &> /dev/null; then \
31+
sudo apt-get update && sudo apt-get install -y default-jre; \
32+
elif command -v yum &> /dev/null; then \
33+
sudo yum install -y java-17-openjdk; \
34+
else \
35+
echo "Please install Java manually: https://adoptium.net/"; \
36+
exit 1; \
37+
fi \
38+
fi \
39+
fi
40+
41+
deps-openapi: deps-java ## Install OpenAPI Generator CLI
42+
@echo "Installing OpenAPI Generator CLI..."
43+
@if ! command -v bun &> /dev/null; then \
44+
echo "bun is not installed. Please install bun first: https://bun.sh/"; \
45+
exit 1; \
46+
fi
47+
bun install @openapitools/openapi-generator-cli -g
48+
49+
deps-spruce: ## Install Spruce
50+
@echo "Installing Spruce..."
51+
@if [ "$(UNAME_S)" = "Darwin" ]; then \
52+
if ! command -v brew &> /dev/null; then \
53+
echo "Homebrew is not installed. Please install it first: https://brew.sh/"; \
54+
exit 1; \
55+
fi; \
56+
brew install starkandwayne/cf/spruce; \
57+
else \
58+
if ! command -v curl &> /dev/null; then \
59+
echo "curl is not installed. Please install it first."; \
60+
exit 1; \
61+
fi; \
62+
curl -sL https://github.com/geofffranks/spruce/releases/download/v1.30.2/spruce-linux-amd64 -o spruce && \
63+
chmod +x spruce && \
64+
sudo mv spruce /usr/local/bin/; \
65+
fi
66+
67+
deps-jq: ## Install jq
68+
@echo "Installing jq..."
69+
@if [ "$(UNAME_S)" = "Darwin" ]; then \
70+
if ! command -v brew &> /dev/null; then \
71+
echo "Homebrew is not installed. Please install it first: https://brew.sh/"; \
72+
exit 1; \
73+
fi; \
74+
brew install jq; \
75+
else \
76+
if ! command -v apt-get &> /dev/null; then \
77+
echo "This installation method only supports apt-based systems. Please install jq manually."; \
78+
exit 1; \
79+
fi; \
80+
sudo apt-get update && sudo apt-get install -y jq; \
81+
fi
82+
83+
check-deps: ## Check if all dependencies are installed
84+
@echo "Checking dependencies..."
85+
@command -v java >/dev/null 2>&1 || { echo "Java is not installed. Run 'make deps-java'"; exit 1; }
86+
@command -v openapi-generator-cli >/dev/null 2>&1 || { echo "openapi-generator-cli is not installed. Run 'make deps-openapi'"; exit 1; }
87+
@command -v spruce >/dev/null 2>&1 || { echo "spruce is not installed. Run 'make deps-spruce'"; exit 1; }
88+
@command -v jq >/dev/null 2>&1 || { echo "jq is not installed. Run 'make deps-jq'"; exit 1; }
89+
@echo "All dependencies are installed!"
90+
91+
prepare: check-deps ## Prepare the OpenAPI specification
92+
@echo "Preparing OpenAPI specification..."
93+
./bin/capi-openapi prepare
94+
95+
gen-openapi-spec: check-deps ## Merge the CAPI OpenAPI specifications
96+
@echo "Merging CAPI OpenAPI specifications..."
97+
./bin/capi-openapi merge
98+
99+
gen-go-client: check-deps gen-openapi-spec ## Generate Go client from OpenAPI spec
100+
@echo "Generating Go client..."
101+
./bin/capi-openapi gen go client
102+
103+
all: deps prepare gen-openapi-spec gen-go-client ## Run all steps to generate the Go client

README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# OpenAPI Specification for CAPI
2+
3+
This directory contains the OpenAPI specification for the CAPI v3 API.
4+
5+
## Flow
6+
1. **Preparation**: Download the CAPI & OpenAPI specs of the designated versions into `specs/{capi,openapi}/{version}.html`.
7+
```bash
8+
make prepare
9+
```
10+
2. **Generate Stubs**: Generate stubs for each CAPI endpoint as defined in the specifications.
11+
12+
3. Merge stubs into the CAPI OpenAPI specification.
13+
```bash
14+
make gen-openapi-spec
15+
```
16+
This will generate the files `capi/{version}.openapi.yaml` and then `capi/{version}.openapi.json`.
17+
18+
4. Generate a client, ex:
19+
```bash
20+
make gen-go-client
21+
```
22+
23+
24+
## Resources
25+
26+
Refer to the following files for more information on specific endpoints and their implementations:
27+
- [CAPI v3.181.0 Spec](https://v3-apidocs.cloudfoundry.org/version/3.181.0/index.html)
28+
- [OpenAPI Spec](https://spec.openapis.org/oas/v3.1.1.html)
29+
- [Learn OpenAPI](https://learn.openapis.org/)
30+
31+

bin/capi-openapi

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
#!/usr/bin/env perl
2+
3+
use strict;
4+
use warnings;
5+
use Getopt::Long;
6+
use FindBin;
7+
use File::Spec;
8+
use File::Basename;
9+
use Cwd qw(abs_path);
10+
use File::Path qw(make_path);
11+
12+
$ENV{CAPI_VERSION} ||= '3.181.0';
13+
$ENV{OPENAPI_VERSION} ||= '3.1.1';
14+
15+
# Get project root directory (one level up from bin/)
16+
my $script_dir = dirname(abs_path($0));
17+
my $project_root = dirname($script_dir);
18+
19+
my $specs = {
20+
'capi' => {
21+
version => $ENV{CAPI_VERSION},
22+
url => "https://v3-apidocs.cloudfoundry.org/version/${ENV{CAPI_VERSION}}/index.html",
23+
},
24+
'openapi' => {
25+
version => $ENV{OPENAPI_VERSION},
26+
url => "https://spec.openapis.org/oas/${ENV{OPENAPI_VERSION}}.html",
27+
},
28+
};
29+
30+
my @endpoints = (qw(
31+
capi
32+
admin
33+
app_usage_events
34+
apps
35+
audit_events
36+
buildpacks
37+
builds
38+
deployments
39+
domains
40+
droplets
41+
environment_variable_groups
42+
feature_flags
43+
info
44+
isolation_segments
45+
organization_quotas
46+
organizations
47+
packages
48+
processes
49+
resource_matches
50+
revisions
51+
roles
52+
root
53+
routes
54+
security_groups
55+
service_brokers
56+
service_credential_bindings
57+
service_instances
58+
service_offerings
59+
service_plan_visibility
60+
service_plans
61+
service_route_bindings
62+
service_usage_events
63+
sidecars
64+
space_features
65+
space_quotas
66+
spaces
67+
stacks
68+
tasks
69+
users
70+
));
71+
72+
sub check_deps {
73+
my @deps = qw(spruce jq openapi-generator);
74+
for my $dep (@deps) {
75+
my $cmd = "which $dep";
76+
my $result = `$cmd`;
77+
if ($result eq '') {
78+
print "Dependency '$dep' not found. Please install it.\n";
79+
if ($dep eq 'openapi-generator') {
80+
print "For openapi-generator, you can install it via:\n";
81+
print 'bun install \@openapitools/openapi-generator -g', "\n";
82+
}
83+
exit 1;
84+
}
85+
}
86+
}
87+
88+
sub fetch_spec {
89+
my ($spec_name, $spec_version, $spec_url) = @_;
90+
my $specs_path = "${project_root}/specs";
91+
my $spec_file = "${specs_path}/${spec_name}/${spec_version}.html";
92+
93+
if (-e $spec_file) {
94+
print "Spec file '${spec_file}' already downloaded.\n";
95+
return
96+
} else {
97+
print "Fetching spec from '${spec_url}' to '${spec_file}'\n";
98+
my $cmd = "curl -sL '${spec_url}' -o '${spec_file}'";
99+
printf("Running command: '%s'\n", $cmd);
100+
system($cmd);
101+
if ($? == -1) {
102+
print "Failed to execute: $!\n";
103+
}
104+
}
105+
}
106+
107+
sub create_capi_files {
108+
my ($capi_version) = @_;
109+
for my $endpoint (@endpoints) {
110+
my $endpoint_file = "${project_root}/capi/${capi_version}/${endpoint}.yml";
111+
`touch ${endpoint_file}`;
112+
}
113+
}
114+
115+
sub prepare {
116+
for my $spec (keys %$specs) {
117+
fetch_spec($spec, $specs->{$spec}->{version}, $specs->{$spec}->{url});
118+
}
119+
create_capi_files($specs->{'capi'}->{version});
120+
}
121+
122+
sub gen_capi_openapi_spec {
123+
my ($capi_version) = @_;
124+
125+
my @yamls = ();
126+
unshift @endpoints, 'capi' unless $endpoints[0] eq 'capi';
127+
for my $endpoint (@endpoints) {
128+
my $endpoint_file = "${project_root}/capi/${capi_version}/${endpoint}.yml";
129+
push @yamls, $endpoint_file;
130+
}
131+
132+
my $capi_openapi_prefix = "${project_root}/capi/${capi_version}.openapi";
133+
my $cmd = "spruce merge -m '".join("' '", @yamls)."' > ${capi_openapi_prefix}.yml";
134+
printf("Running command: '%s'\n", $cmd);
135+
system($cmd);
136+
if ($? == -1) {
137+
print "Failed to execute: $!\n";
138+
}
139+
$cmd = "spruce json ${capi_openapi_prefix}.yml | jq > ${capi_openapi_prefix}.json";
140+
printf("Running command: '%s'\n", $cmd);
141+
system($cmd);
142+
if ($? == -1) {
143+
print "Failed to execute: $!\n";
144+
}
145+
}
146+
147+
sub generate_go_client {
148+
my ($capi_version) = @_;
149+
150+
my $spec_file = "${project_root}/capi/${capi_version}.openapi.json";
151+
my $output_dir = "${project_root}/clients/go";
152+
make_path($output_dir) unless -d $output_dir;
153+
154+
my $cmd = "openapi-generator generate " .
155+
"-i $spec_file " .
156+
"-g go " .
157+
"-o $output_dir " .
158+
"--additional-properties=packageName=capiclient,isGoSubmodule=true,generateInterfaces=true";
159+
# TODO: investigate what additional properties we want here.
160+
161+
printf("Generating Go client...\n");
162+
printf("Running command: '%s'\n", $cmd);
163+
system($cmd);
164+
if ($? == -1) {
165+
print "Failed to execute: $!\n";
166+
exit 1;
167+
}
168+
print "Go client generated successfully in $output_dir\n";
169+
}
170+
171+
sub convert {
172+
print "Convert functionality not implemented yet\n";
173+
exit 1;
174+
}
175+
176+
sub main {
177+
my $command = shift @ARGV || '';
178+
my $subcommand = shift @ARGV || '';
179+
my $type = shift @ARGV || '';
180+
181+
if ($command eq 'prepare') {
182+
prepare();
183+
}
184+
elsif ($command eq 'convert') {
185+
convert();
186+
}
187+
elsif ($command eq 'gen' && $subcommand eq 'openapi' && $type eq 'spec') {
188+
check_deps();
189+
gen_capi_openapi_spec($specs->{'capi'}->{version});
190+
}
191+
elsif ($command eq 'gen' && $subcommand eq 'go' && $type eq 'client') {
192+
check_deps();
193+
generate_go_client($specs->{'capi'}->{version});
194+
}
195+
else {
196+
print "Usage: $0 <command> [subcommand] [type]\n";
197+
print "Available commands:\n";
198+
print " prepare - Fetch specs and create CAPI files\n";
199+
print " convert - Convert spec sections from published api to openapi. (Not implemented yet - still manual)\n";
200+
print " gen openapi spec - Generate CAPI OpenAPI specs by merging prepared and converted stubs.\n";
201+
print " gen go client - Generate Go client from OpenAPI spec\n";
202+
exit 1;
203+
}
204+
}
205+
206+
main();
207+
208+
__END__
209+

capi/3.181.0/admin.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
paths:
2+
/v3/admin/actions/clear_buildpack_cache:
3+
post:
4+
summary: Clear buildpack cache
5+
description: This endpoint will delete all of the existing buildpack caches in the blobstore.
6+
responses:
7+
'202':
8+
description: The buildpack cache clearing has been accepted.
9+
'default':
10+
description: An unexpected error.
11+
security:
12+
- Admin: []
13+

0 commit comments

Comments
 (0)