uffizzi_core 0.1.11 → 0.1.12

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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -0
  3. data/app/controllers/uffizzi_core/api/cli/v1/account/credentials_controller.rb +3 -3
  4. data/app/controllers/uffizzi_core/api/cli/v1/projects_controller.rb +68 -2
  5. data/app/forms/uffizzi_core/api/cli/v1/deployment/create_form.rb +1 -0
  6. data/app/forms/uffizzi_core/api/cli/v1/project/create_form.rb +7 -0
  7. data/app/lib/uffizzi_core/concerns/models/activity_item.rb +39 -0
  8. data/app/lib/uffizzi_core/concerns/models/credential.rb +65 -0
  9. data/app/lib/uffizzi_core/concerns/models/repo.rb +33 -0
  10. data/app/lib/uffizzi_core/rbac/user_access_service.rb +24 -0
  11. data/app/models/uffizzi_core/project.rb +9 -2
  12. data/app/policies/uffizzi_core/api/cli/v1/projects_policy.rb +12 -0
  13. data/app/serializers/uffizzi_core/api/cli/v1/project_serializer/compose_file_serializer.rb +7 -0
  14. data/app/serializers/uffizzi_core/api/cli/v1/project_serializer/deployment_serializer.rb +13 -0
  15. data/app/serializers/uffizzi_core/api/cli/v1/project_serializer.rb +24 -1
  16. data/app/serializers/uffizzi_core/api/cli/v1/projects/deployment_serializer/container_serializer.rb +2 -1
  17. data/app/serializers/uffizzi_core/api/cli/v1/projects/deployments/container_serializer.rb +1 -1
  18. data/app/serializers/uffizzi_core/api/cli/v1/short_project_serializer.rb +7 -0
  19. data/app/serializers/uffizzi_core/controller/deploy_containers/container_serializer.rb +16 -1
  20. data/app/services/uffizzi_core/compose_file/builders/container_builder_service.rb +11 -7
  21. data/app/services/uffizzi_core/compose_file/config_option_service.rb +4 -0
  22. data/app/services/uffizzi_core/compose_file/parsers/services/healthcheck_parser_service.rb +73 -0
  23. data/app/services/uffizzi_core/compose_file/services_options_service.rb +2 -0
  24. data/app/services/uffizzi_core/compose_file/template_service.rb +4 -4
  25. data/app/services/uffizzi_core/compose_file_service.rb +1 -1
  26. data/app/services/uffizzi_core/deployment_service.rb +17 -5
  27. data/app/services/uffizzi_core/manage_activity_items_service.rb +1 -1
  28. data/app/services/uffizzi_core/project_service.rb +10 -0
  29. data/app/services/uffizzi_core/starter_template_service.rb +200 -0
  30. data/app/services/uffizzi_core/user_generator_service.rb +11 -5
  31. data/config/locales/en.yml +2 -0
  32. data/config/routes.rb +11 -1
  33. data/db/migrate/20220419074956_add_health_check_to_containers.rb +7 -0
  34. data/lib/tasks/uffizzi_core_tasks.rake +1 -1
  35. data/lib/uffizzi_core/version.rb +1 -1
  36. data/swagger/v1/swagger.json +174 -2
  37. metadata +12 -2
@@ -8,12 +8,12 @@ module UffizziCore::DeploymentService
8
8
  building: :building,
9
9
  deploying: :deploying,
10
10
  failed: :failed,
11
+ queued: :queued,
11
12
  }.freeze
12
13
 
13
14
  class << self
14
15
  def create_from_compose(compose_file, project, user)
15
16
  deployment_attributes = ActionController::Parameters.new(compose_file.template.payload)
16
-
17
17
  deployment_form = UffizziCore::Api::Cli::V1::Deployment::CreateForm.new(deployment_attributes)
18
18
  deployment_form.assign_dependences!(project, user)
19
19
  deployment_form.compose_file = compose_file
@@ -103,8 +103,9 @@ module UffizziCore::DeploymentService
103
103
  pull_request_payload = continuous_preview_payload['pull_request']
104
104
  repo_name = pull_request_payload['repository_full_name'].split('/').last
105
105
  deployment_name = name(deployment)
106
+ subdomain = "pr#{pull_request_payload['id']}-#{deployment_name}-#{repo_name}-#{project.slug}"
106
107
 
107
- "pr#{pull_request_payload['id']}-#{deployment_name}.#{repo_name}.#{project.slug}"
108
+ format_subdomain(subdomain)
108
109
  end
109
110
 
110
111
  def build_docker_continuous_preview_subdomain(deployment)
@@ -114,14 +115,17 @@ module UffizziCore::DeploymentService
114
115
  repo_name = docker_payload['image'].split('/').last.gsub('_', '-')
115
116
  image_tag = docker_payload['tag'].gsub('_', '-')
116
117
  deployment_name = name(deployment)
118
+ subdomain = "#{image_tag}-#{deployment_name}-#{repo_name}-#{project.slug}"
117
119
 
118
- "#{image_tag}-#{deployment_name}.#{repo_name}.#{project.slug}"
120
+ format_subdomain(subdomain)
119
121
  end
120
122
 
121
123
  def build_default_subdomain(deployment)
122
124
  deployment_name = name(deployment)
123
125
  slug = deployment.project.slug.to_s
124
- "#{deployment_name}.#{slug}"
126
+ subdomain = "#{deployment_name}-#{slug}"
127
+
128
+ format_subdomain(subdomain)
125
129
  end
126
130
 
127
131
  def build_preview_url(deployment)
@@ -238,10 +242,11 @@ module UffizziCore::DeploymentService
238
242
 
239
243
  def deployment_process_status(deployment)
240
244
  containers = deployment.active_containers
241
- activity_items = containers.map { |container| container.activity_items.order_by_id.last }
245
+ activity_items = containers.map { |container| container.activity_items.order_by_id.last }.compact
242
246
  events = activity_items.map { |activity_item| activity_item.events.order_by_id.last&.state }
243
247
  events = events.flatten.uniq
244
248
 
249
+ return DEPLOYMENT_PROCESS_STATUSES[:queued] if containers.present? && events.empty?
245
250
  return DEPLOYMENT_PROCESS_STATUSES[:failed] if events.include?(UffizziCore::Event.state.failed)
246
251
  return DEPLOYMENT_PROCESS_STATUSES[:building] if events.include?(UffizziCore::Event.state.building)
247
252
 
@@ -291,5 +296,12 @@ module UffizziCore::DeploymentService
291
296
  container.variables.push(*envs)
292
297
  end
293
298
  end
299
+
300
+ def format_subdomain(full_subdomain_name)
301
+ subdomain_length_limit = Settings.deployment.subdomain.length_limit
302
+ return full_subdomain_name if full_subdomain_name.length <= subdomain_length_limit
303
+
304
+ full_subdomain_name.slice(0, subdomain_length_limit)
305
+ end
294
306
  end
295
307
  end
@@ -118,7 +118,7 @@ class UffizziCore::ManageActivityItemsService
118
118
 
119
119
  Rails.logger.info("manage_activity_items get_status dep_id=#{container.deployment.id} pod_container_status: #{pod_container_status}")
120
120
  Rails.logger.info("manage_activity_items get_status dep_id=#{container.deployment.id} state: #{state}")
121
- ap pod_container[:state]
121
+ ap(pod_container[:state])
122
122
 
123
123
  case pod_container_status.to_sym
124
124
  when :running
@@ -34,5 +34,15 @@ module UffizziCore::ProjectService
34
34
 
35
35
  UffizziCore::ComposeFile::ErrorsService.update_compose_errors!(compose_file, new_errors, compose_file.content)
36
36
  end
37
+
38
+ def add_users_to_project!(project, current_user)
39
+ user_projects = []
40
+
41
+ current_user.organizational_account.memberships.where(role: UffizziCore::Membership.role.admin).map do |membership|
42
+ user_projects << { project: project, user: membership.user, role: UffizziCore::UserProject.role.admin }
43
+ end
44
+
45
+ UffizziCore::UserProject.create!(user_projects)
46
+ end
37
47
  end
38
48
  end
@@ -0,0 +1,200 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::StarterTemplateService
4
+ class << self
5
+ def create(project, user)
6
+ voting_app_config_file = create_config_file(project, user)
7
+ create_containers(project, user, voting_app_config_file)
8
+ end
9
+
10
+ private
11
+
12
+ def create_config_file(project, user)
13
+ starter_template_config_file = config_file_attributes
14
+
15
+ starter_template_config_file_params = ActionController::Parameters.new(starter_template_config_file)
16
+
17
+ starter_template_config_file_form = UffizziCore::Api::Cli::V1::ConfigFile::CreateForm.new(starter_template_config_file_params)
18
+ starter_template_config_file_form.project = project
19
+ starter_template_config_file_form.added_by = user
20
+ starter_template_config_file_form.creation_source = UffizziCore::ConfigFile.creation_source.system
21
+ starter_template_config_file_form.save
22
+
23
+ starter_template_config_file_form
24
+ end
25
+
26
+ def create_containers(project, user, voting_app_config_file)
27
+ starter_template_containers = {
28
+ name: 'Voting App (from base images)',
29
+ payload: {
30
+ containers_attributes: containers_attributes(voting_app_config_file),
31
+ },
32
+ }
33
+
34
+ starter_template = ActionController::Parameters.new(starter_template_containers)
35
+
36
+ starter_template_form = UffizziCore::Api::Cli::V1::Template::CreateForm.new(starter_template)
37
+ starter_template_form.creation_source = UffizziCore::Template.creation_source.system
38
+ starter_template_form.added_by = user
39
+ starter_template_form.project = project
40
+ starter_template_form.save
41
+ end
42
+
43
+ def containers_attributes(voting_app_config_file)
44
+ [
45
+ postgres_container_attributes,
46
+ nginx_container_attributes(voting_app_config_file),
47
+ redis_container_attributes,
48
+ example_worker_container_attributes,
49
+ example_result_container_attributes,
50
+ example_vote_container_attributes,
51
+ ]
52
+ end
53
+
54
+ def postgres_container_attributes
55
+ {
56
+ image: 'library/postgres',
57
+ tag: '9.6',
58
+ port: nil,
59
+ public: false,
60
+ memory_limit: 250,
61
+ memory_request: 250,
62
+ receive_incoming_requests: false,
63
+ continuously_deploy: 'disabled',
64
+ secret_variables: nil,
65
+ variables: [
66
+ {
67
+ name: 'POSTGRES_USER',
68
+ value: 'postgres',
69
+ },
70
+ {
71
+ name: 'POSTGRES_PASSWORD',
72
+ value: 'postgres',
73
+ },
74
+ ],
75
+ repo_attributes: repo_attributes('library/postgres'),
76
+ container_config_files_attributes: [],
77
+ }
78
+ end
79
+
80
+ def nginx_container_attributes(voting_app_config_file)
81
+ {
82
+ image: 'library/nginx',
83
+ tag: 'latest',
84
+ port: 8080,
85
+ public: true,
86
+ memory_limit: 125,
87
+ memory_request: 125,
88
+ receive_incoming_requests: true,
89
+ continuously_deploy: 'disabled',
90
+ secret_variables: nil,
91
+ variables: nil,
92
+ repo_attributes: repo_attributes('library/nginx'),
93
+ container_config_files_attributes: [{
94
+ config_file_id: voting_app_config_file.id,
95
+ mount_path: '/etc/nginx/conf.d/',
96
+ }],
97
+ }
98
+ end
99
+
100
+ def redis_container_attributes
101
+ {
102
+ image: 'library/redis',
103
+ tag: 'latest',
104
+ port: nil,
105
+ public: false,
106
+ memory_limit: 125,
107
+ memory_request: 125,
108
+ receive_incoming_requests: false,
109
+ continuously_deploy: 'disabled',
110
+ secret_variables: nil,
111
+ variables: nil,
112
+ repo_attributes: repo_attributes('library/redis'),
113
+ container_config_files_attributes: [],
114
+ }
115
+ end
116
+
117
+ def example_worker_container_attributes
118
+ {
119
+ image: 'uffizzicloud/example-worker',
120
+ tag: 'latest',
121
+ port: nil,
122
+ public: false,
123
+ memory_limit: 250,
124
+ memory_request: 250,
125
+ receive_incoming_requests: false,
126
+ continuously_deploy: 'disabled',
127
+ secret_variables: nil,
128
+ variables: nil,
129
+ repo_attributes: repo_attributes('uffizzicloud/example-worker'),
130
+ container_config_files_attributes: [],
131
+ }
132
+ end
133
+
134
+ def example_result_container_attributes
135
+ {
136
+ image: 'uffizzicloud/example-result',
137
+ tag: 'latest',
138
+ port: nil,
139
+ public: false,
140
+ memory_limit: 125,
141
+ memory_request: 125,
142
+ receive_incoming_requests: false,
143
+ continuously_deploy: 'disabled',
144
+ secret_variables: nil,
145
+ variables: nil,
146
+ repo_attributes: repo_attributes('uffizzicloud/example-result'),
147
+ container_config_files_attributes: [],
148
+ }
149
+ end
150
+
151
+ def example_vote_container_attributes
152
+ {
153
+ image: 'uffizzicloud/example-vote',
154
+ tag: 'latest',
155
+ port: nil,
156
+ public: false,
157
+ memory_limit: 250,
158
+ memory_request: 250,
159
+ receive_incoming_requests: false,
160
+ continuously_deploy: 'disabled',
161
+ secret_variables: nil,
162
+ variables: nil,
163
+ repo_attributes: repo_attributes('uffizzicloud/example-vote'),
164
+ container_config_files_attributes: [],
165
+ }
166
+ end
167
+
168
+ def config_file_attributes
169
+ {
170
+ filename: 'vote.conf',
171
+ kind: 'config_map',
172
+ payload: "server {
173
+ listen 8080;
174
+ server_name example.com;
175
+ location / {
176
+ proxy_pass http://127.0.0.1:80/;
177
+ }
178
+ location /vote/ {
179
+ proxy_pass http://127.0.0.1:8888/;
180
+ }
181
+ }",
182
+ }
183
+ end
184
+
185
+ def repo_attributes(image)
186
+ namespace, name = image.split('/')
187
+ {
188
+ namespace: namespace,
189
+ name: name,
190
+ slug: name,
191
+ type: 'UffizziCore::Repo::DockerHub',
192
+ description: '',
193
+ is_private: false,
194
+ repository_id: nil,
195
+ branch: '',
196
+ kind: nil,
197
+ }
198
+ end
199
+ end
200
+ end
@@ -6,6 +6,12 @@ class UffizziCore::UserGeneratorService
6
6
  DEFAULT_ACCOUNT_NAME = 'default'
7
7
 
8
8
  class << self
9
+ def safe_generate(email, password, project_name)
10
+ generate(email, password, project_name)
11
+ rescue StandardError => e
12
+ puts e.message
13
+ end
14
+
9
15
  def generate(email, password, project_name)
10
16
  user_attributes = build_user_attributes(email, password)
11
17
  project_attributes = build_project_attributes(project_name)
@@ -31,19 +37,17 @@ class UffizziCore::UserGeneratorService
31
37
 
32
38
  if email.present?
33
39
  user_attributes[:email] = email
34
- else
40
+ elsif IO::console.present?
35
41
  IO::console.write("Enter User Email (default: #{DEFAULT_USER_EMAIL}): ")
36
42
  user_attributes[:email] = IO::console.gets.strip.presence || DEFAULT_USER_EMAIL
37
43
  end
38
44
 
39
45
  user_attributes[:password] = if password.present?
40
46
  password
41
- else
47
+ elsif IO::console.present?
42
48
  IO::console.getpass('Enter Password: ')
43
49
  end
44
50
 
45
- abort('password can\'t be blank') if user_attributes[:password].blank?
46
-
47
51
  user_attributes
48
52
  end
49
53
 
@@ -53,9 +57,11 @@ class UffizziCore::UserGeneratorService
53
57
  }
54
58
  if project_name.present?
55
59
  project_attributes[:name] = project_name
56
- else
60
+ elsif IO::console.present?
57
61
  IO::console.write("Enter Project Name (default: #{DEFAULT_PROJECT_NAME}): ")
58
62
  project_attributes[:name] = IO::console.gets.strip.presence || DEFAULT_PROJECT_NAME
63
+ else
64
+ project_attributes[:name] = DEFAULT_PROJECT_NAME
59
65
  end
60
66
 
61
67
  project_attributes[:slug] = prepare_project_slug(project_attributes[:name])
@@ -57,8 +57,10 @@ en:
57
57
  continuous_preview_in_service_level: The option '%{option}' is not supported for service-level. Use 'x-uffizzi-continuous-preview' instead
58
58
  file_already_exists: A compose file already exists for this project. Run 'uffizzi compose update' to update this file or 'uffizzi compose rm' to remove it. For more options, see 'uffizzi compose --help'
59
59
  invalid_healthcheck_command: "Service '%{name}' defines an invalid healthcheck: when 'test' is a list the first item must be either NONE, CMD or CMD-SHELL"
60
+ invalid_time_interval: The time interval should be in the following format '{hours}h{minutes}m{seconds}s'. At least one value must be present.
60
61
  string_or_array_error: "'%{option}' contains an invalid type, it should be a string, or an array"
61
62
  not_implemented: "'%{option}' option is not implemented"
63
+ infinite_recursion: "Found infinite recursion for key '%{key}'"
62
64
  secrets:
63
65
  duplicates_exists: Secret with key %{secrets} already exist.
64
66
  invalid_key_length: A secret key must be no longer than 256 characters.
data/config/routes.rb CHANGED
@@ -15,13 +15,23 @@ UffizziCore::Engine.routes.draw do
15
15
  post :google
16
16
  end
17
17
 
18
- resources :projects, only: ['index'], param: :slug do
18
+ resources :projects, only: ['index', 'show', 'create', 'destroy'], param: :slug do
19
19
  scope module: :projects do
20
20
  resource :compose_file, only: ['show', 'create', 'destroy']
21
21
  resources :deployments, only: ['index', 'show', 'create', 'destroy', 'update'] do
22
22
  post :deploy_containers, on: :member
23
23
  scope module: :deployments do
24
24
  resources :activity_items, only: ['index']
25
+ resources :containers, only: ['index'], param: :name do
26
+ scope module: :containers do
27
+ resources :logs, only: ['index']
28
+ resources :builds, only: [] do
29
+ collection do
30
+ get :logs
31
+ end
32
+ end
33
+ end
34
+ end
25
35
  resources :events, only: ['index']
26
36
  resources :containers, only: ['index'], param: :name do
27
37
  scope module: :containers do
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddHealthCheckToContainers < ActiveRecord::Migration[6.1]
4
+ def change
5
+ add_column :uffizzi_core_containers, :healthcheck, :jsonb
6
+ end
7
+ end
@@ -14,6 +14,6 @@ namespace :uffizzi_core do
14
14
 
15
15
  desc 'Create a new user'
16
16
  task create_user: :environment do
17
- UffizziCore::UserGeneratorService.generate(ENV['UFFIZZI_USER_EMAIL'], ENV['UFFIZZI_USER_PASSWORD'], ENV['UFFIZZI_PROJECT_NAME'])
17
+ UffizziCore::UserGeneratorService.safe_generate(ENV['UFFIZZI_USER_EMAIL'], ENV['UFFIZZI_USER_PASSWORD'], ENV['UFFIZZI_PROJECT_NAME'])
18
18
  end
19
19
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module UffizziCore
4
- VERSION = '0.1.11'
4
+ VERSION = '0.1.12'
5
5
  end
@@ -80,7 +80,7 @@
80
80
  "default": {
81
81
  "description": "Create account credential",
82
82
  "examples": {
83
- "application/json": "type can be one of UffizziCore::Credential::Amazon, UffizziCore::Credential::Azure, UffizziCore::Credential::DockerHub, UffizziCore::Credential::Google, UffizziCore::Credential::GithubContainerRegistry"
83
+ "application/json": "type can be one of UffizziCore::Credential::Amazon, UffizziCore::Credential::Azure,\nUffizziCore::Credential::DockerHub, UffizziCore::Credential::Google, UffizziCore::Credential::GithubContainerRegistry"
84
84
  }
85
85
  },
86
86
  "201": {
@@ -1134,6 +1134,9 @@
1134
1134
  "properties": {
1135
1135
  "slug": {
1136
1136
  "type": "string"
1137
+ },
1138
+ "name": {
1139
+ "type": "string"
1137
1140
  }
1138
1141
  }
1139
1142
  }
@@ -1149,6 +1152,143 @@
1149
1152
  "summary": "Get projects of current user",
1150
1153
  "x-controller": "uffizzi_core/api/cli/v1/projects",
1151
1154
  "x-action": "index"
1155
+ },
1156
+ "post": {
1157
+ "tags": [
1158
+ "Project"
1159
+ ],
1160
+ "operationId": "Project-create",
1161
+ "parameters": [
1162
+ {
1163
+ "name": "params",
1164
+ "description": "params",
1165
+ "required": true,
1166
+ "in": "body",
1167
+ "schema": {
1168
+ "type": "object",
1169
+ "properties": {
1170
+ "name": {
1171
+ "type": "string"
1172
+ },
1173
+ "slug": {
1174
+ "type": "string"
1175
+ },
1176
+ "description": {
1177
+ "type": "string"
1178
+ }
1179
+ }
1180
+ }
1181
+ }
1182
+ ],
1183
+ "responses": {
1184
+ "200": {
1185
+ "description": "OK",
1186
+ "schema": {
1187
+ "type": "object",
1188
+ "properties": {
1189
+ "project": {
1190
+ "$ref": "#/definitions/Project"
1191
+ }
1192
+ }
1193
+ }
1194
+ },
1195
+ "404": {
1196
+ "description": "Not Found"
1197
+ },
1198
+ "401": {
1199
+ "description": "Not authorized"
1200
+ },
1201
+ "422": {
1202
+ "description": "Unprocessable entity",
1203
+ "schema": {
1204
+ "type": "object",
1205
+ "properties": {
1206
+ "errors": {
1207
+ "type": "object",
1208
+ "properties": {
1209
+ "password": {
1210
+ "type": "string"
1211
+ }
1212
+ }
1213
+ }
1214
+ }
1215
+ }
1216
+ }
1217
+ },
1218
+ "description": "Create a project",
1219
+ "summary": "Create a project",
1220
+ "x-controller": "uffizzi_core/api/cli/v1/projects",
1221
+ "x-action": "create"
1222
+ }
1223
+ },
1224
+ "/api/cli/v1/projects/{slug}": {
1225
+ "get": {
1226
+ "tags": [
1227
+ "Project"
1228
+ ],
1229
+ "operationId": "Project-show",
1230
+ "parameters": [
1231
+ {
1232
+ "name": "slug",
1233
+ "description": "Scope response to slug",
1234
+ "required": true,
1235
+ "in": "path",
1236
+ "type": "string"
1237
+ }
1238
+ ],
1239
+ "responses": {
1240
+ "200": {
1241
+ "description": "OK",
1242
+ "schema": {
1243
+ "type": "object",
1244
+ "properties": {
1245
+ "project": {
1246
+ "$ref": "#/definitions/Project"
1247
+ }
1248
+ }
1249
+ }
1250
+ },
1251
+ "404": {
1252
+ "description": "Not Found"
1253
+ },
1254
+ "401": {
1255
+ "description": "Not authorized"
1256
+ }
1257
+ },
1258
+ "description": "Get a project by slug",
1259
+ "summary": "Get a project by slug",
1260
+ "x-controller": "uffizzi_core/api/cli/v1/projects",
1261
+ "x-action": "show"
1262
+ },
1263
+ "delete": {
1264
+ "tags": [
1265
+ "Project"
1266
+ ],
1267
+ "operationId": "Project-destroy",
1268
+ "parameters": [
1269
+ {
1270
+ "name": "slug",
1271
+ "description": "Scope response to slug",
1272
+ "required": true,
1273
+ "in": "path",
1274
+ "type": "string"
1275
+ }
1276
+ ],
1277
+ "responses": {
1278
+ "204": {
1279
+ "description": "No content"
1280
+ },
1281
+ "404": {
1282
+ "description": "Not Found"
1283
+ },
1284
+ "401": {
1285
+ "description": "Not authorized"
1286
+ }
1287
+ },
1288
+ "description": "Delete a project",
1289
+ "summary": "Delete a project",
1290
+ "x-controller": "uffizzi_core/api/cli/v1/projects",
1291
+ "x-action": "destroy"
1152
1292
  }
1153
1293
  },
1154
1294
  "/api/cli/v1/session": {
@@ -1475,11 +1615,43 @@
1475
1615
  }
1476
1616
  }
1477
1617
  },
1478
- "UffizziCore_Project": {
1618
+ "Project": {
1479
1619
  "type": "object",
1480
1620
  "properties": {
1481
1621
  "slug": {
1482
1622
  "type": "string"
1623
+ },
1624
+ "name": {
1625
+ "type": "string"
1626
+ },
1627
+ "description": {
1628
+ "type": "string"
1629
+ },
1630
+ "created_at": {
1631
+ "type": "string",
1632
+ "format": "date-time"
1633
+ },
1634
+ "secrets": {
1635
+ "type": "string"
1636
+ },
1637
+ "default_compose": {
1638
+ "type": "object",
1639
+ "properties": {
1640
+ "source": {
1641
+ "type": "string"
1642
+ }
1643
+ }
1644
+ },
1645
+ "deployments": {
1646
+ "type": "object",
1647
+ "properties": {
1648
+ "id": {
1649
+ "type": "integer"
1650
+ },
1651
+ "domain": {
1652
+ "type": "string"
1653
+ }
1654
+ }
1483
1655
  }
1484
1656
  }
1485
1657
  }