Skip to content

Commit b7a5b9b

Browse files
authored
Merge pull request #2858 from OpenC3/feat/app-store-updates
App store plugin versions
2 parents 8990a02 + 18e289b commit b7a5b9b

File tree

11 files changed

+338
-116
lines changed

11 files changed

+338
-116
lines changed

openc3-cosmos-cmd-tlm-api/app/controllers/plugins_controller.rb

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,14 @@ def initialize
3131
@model_class = OpenC3::PluginModel
3232
end
3333

34-
def check_localhost_reachability(gem_url, store_id)
34+
def check_localhost_reachability(gem_url, store_plugin_id)
3535
uri = URI.parse(gem_url)
3636
return gem_url unless ['localhost', '127.0.0.1'].include? uri.host
3737

3838
api_key_setting = OpenC3::SettingModel.get(name: 'store_api_key', scope: 'DEFAULT')
3939
api_key = api_key_setting['data'] if api_key_setting
4040

41-
test_url = "http://#{uri.host}:#{uri.port}/api/v1.1/cosmos_plugins/#{store_id}"
41+
test_url = "http://#{uri.host}:#{uri.port}/api/v1.1/cosmos_plugins/#{store_plugin_id}"
4242
begin
4343
uri_obj = URI(test_url)
4444
req = Net::HTTP::Get.new(uri_obj)
@@ -79,19 +79,23 @@ def show
7979
store_plugins = JSON.parse(store_plugins)
8080
if store_plugins.is_a?(Array) # as opposed to a Hash, which indicates an error
8181
plugins.each do |plugin_name, plugin|
82-
if plugin['store_id']
83-
store_data = store_plugins.find { |store_plugin| store_plugin['id'] == plugin['store_id'] }
84-
plugin.merge!(store_data) if store_data
82+
if plugin['store_plugin_id']
83+
store_plugin = store_plugins.find { |store_plugin| store_plugin['id'] == plugin['store_plugin_id'] }
84+
store_version_id = plugin['store_version_id'] || store_plugin['current_version_id']
85+
store_version = store_plugin['versions'].find { |store_version| store_version['id'] == store_version_id }
86+
plugin.merge!(store_version) if store_version
8587
end
8688
end
8789
end
8890

8991
render json: plugins
9092
else
9193
plugin = @model_class.get(name: params[:id], scope: params[:scope])
92-
if plugin && plugin['store_id']
93-
store_data = OpenC3::PluginStoreModel.get_by_id(plugin['store_id'])
94-
plugin.merge!(store_data) if store_data
94+
if plugin && plugin['store_plugin_id']
95+
store_plugin = OpenC3::PluginStoreModel.get_by_id(plugin['store_plugin_id'])
96+
store_version_id = plugin['store_version_id'] || store_plugin['current_version_id']
97+
store_version = store_plugin['versions'].find { |store_version| store_version['id'] == store_version_id }
98+
plugin.merge!(store_version) if store_version
9599
end
96100

97101
render json: plugin
@@ -104,15 +108,20 @@ def show
104108
# Add a new plugin
105109
def create(update = false)
106110
return unless authorization('admin')
107-
file = if params[:store_id]
108-
store_data = OpenC3::PluginStoreModel.get_by_id(params[:store_id])
109-
if store_data.nil? || store_data['gem_url'].nil?
110-
render json: { status: 'error', message: 'Unable to fetch requested plugin.' }, status: 500
111+
file = if params[:store_plugin_id]
112+
store_data = OpenC3::PluginStoreModel.get_by_id(params[:store_plugin_id])
113+
unless store_data.nil?
114+
store_version_id = params[:store_version_id] || store_data['current_version_id']
115+
store_version_id = Integer(store_version_id)
116+
store_version = store_data['versions'].find { |version| version['id'] == store_version_id }
117+
end
118+
if store_version.nil? || store_version['gem_url'].nil?
119+
render json: { status: 'error', message: 'Unable to fetch requested plugin version.' }, status: 500
111120
return
112121
end
113122

114123
# Try to find the correct hostname (in case it's localhost and needs to be host.docker.internal)
115-
adjusted_gem_url = check_localhost_reachability(store_data['gem_url'], params[:store_id])
124+
adjusted_gem_url = check_localhost_reachability(store_version['gem_url'], params[:store_plugin_id])
116125
if adjusted_gem_url.nil?
117126
render json: { status: 'error', message: 'Gem could not be downloaded. Host is not reachable.' }, status: 500
118127
return
@@ -127,13 +136,13 @@ def create(update = false)
127136
tempfile = Down.download(adjusted_gem_url)
128137
end
129138
checksum = Digest::SHA256.file(tempfile.path).hexdigest.downcase
130-
expected = store_data['checksum'].downcase
139+
expected = store_version['checksum'].downcase
131140
unless checksum == expected
132141
render json: { status: 'error', message: "Checksum verification failed. Expected #{expected} but got #{checksum}" }, status: 500
133142
return
134143
end
135144

136-
original_filename = File.basename(store_data['gem_filename'])
145+
original_filename = File.basename(store_version['gem_filename'])
137146
tempfile
138147
else
139148
params[:plugin]
@@ -149,9 +158,9 @@ def create(update = false)
149158
gem_file_path = temp_dir + '/' + original_filename
150159
FileUtils.cp(tempfile.path, gem_file_path)
151160
if @existing_model
152-
result = OpenC3::PluginModel.install_phase1(gem_file_path, existing_variables: @existing_model['variables'], existing_plugin_txt_lines: @existing_model['plugin_txt_lines'], store_id: params[:store_id], scope: scope)
161+
result = OpenC3::PluginModel.install_phase1(gem_file_path, existing_variables: @existing_model['variables'], existing_plugin_txt_lines: @existing_model['plugin_txt_lines'], store_plugin_id: params[:store_plugin_id], store_version_id: params[:store_version_id], scope: scope)
153162
else
154-
result = OpenC3::PluginModel.install_phase1(gem_file_path, store_id: params[:store_id], scope: scope)
163+
result = OpenC3::PluginModel.install_phase1(gem_file_path, store_plugin_id: params[:store_plugin_id], store_version_id: params[:store_version_id], scope: scope)
155164
end
156165
render json: result
157166
rescue OpenC3::EmptyGemFileError => error

openc3-cosmos-cmd-tlm-api/spec/controllers/plugins_controller_spec.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,8 @@
132132
"/tmp/test/test-plugin.gem",
133133
existing_variables: {"VAR1" => "value1"},
134134
existing_plugin_txt_lines: ["line1", "line2"],
135-
store_id: nil,
135+
store_plugin_id: nil,
136+
store_version_id: nil,
136137
scope: "DEFAULT"
137138
).and_return(install_result)
138139

@@ -230,7 +231,8 @@
230231
"/tmp/test/test-plugin.gem",
231232
existing_variables: {"VAR1" => "existing_value"},
232233
existing_plugin_txt_lines: ["existing_line"],
233-
store_id: nil,
234+
store_plugin_id: nil,
235+
store_version_id: nil,
234236
scope: "DEFAULT"
235237
).and_return(install_result)
236238

openc3-cosmos-init/plugins/packages/openc3-vue-common/src/plugins/plugin-store/PluginCard.vue

Lines changed: 74 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -12,50 +12,63 @@
1212
-->
1313

1414
<template>
15-
<plugin-details-dialog v-bind="plugin" @trigger-install="install">
16-
<template #activator="{ props }">
17-
<v-card v-bind="props" height="350" class="d-flex flex-column">
18-
<v-card-title class="d-flex align-center justify-content-space-between">
19-
{{ title }}
20-
</v-card-title>
21-
<v-card-subtitle
22-
class="d-flex align-center justify-content-space-between"
23-
>
24-
<div>{{ author }}</div>
25-
<!--
26-
<v-spacer />
27-
<v-rating
28-
:model-value="rating"
29-
density="compact"
30-
size="small"
31-
readonly
32-
half-increments
33-
/>
34-
-->
35-
</v-card-subtitle>
36-
<v-card-text>
37-
<v-img v-if="image_url" :src="image_url" max-height="160" />
38-
<div
39-
:class="{
40-
'truncate-description': true,
41-
'truncate-2': !!image_url,
42-
'truncate-10': !image_url,
43-
}"
44-
v-text="description"
45-
/>
46-
</v-card-text>
47-
<v-spacer />
48-
<v-card-actions class="flex-wrap">
49-
<v-btn
50-
text="Install"
51-
append-icon="mdi-puzzle-plus"
52-
variant="elevated"
53-
@click.stop="install"
54-
/>
55-
</v-card-actions>
56-
</v-card>
57-
</template>
58-
</plugin-details-dialog>
15+
<v-card height="350" class="d-flex flex-column" @click="openDialog">
16+
<v-card-title class="d-flex align-center justify-content-space-between">
17+
{{ title }}
18+
</v-card-title>
19+
<v-card-subtitle class="d-flex align-center justify-content-space-between">
20+
<div>
21+
By <strong>{{ author }}</strong>
22+
<span v-if="author_extra?.badge_text" class="ml-1 font-italic">
23+
{{ author_extra.badge_text }}
24+
</span>
25+
<v-icon
26+
v-if="author_extra?.badge_icon"
27+
:icon="author_extra.badge_icon"
28+
:size="18"
29+
class="ml-1"
30+
/>
31+
</div>
32+
<!--
33+
<v-spacer />
34+
<v-rating
35+
:model-value="rating"
36+
density="compact"
37+
size="small"
38+
readonly
39+
half-increments
40+
/>
41+
-->
42+
</v-card-subtitle>
43+
<v-card-text>
44+
<div class="plugin-image-backdrop">
45+
<v-img v-if="image_url" :src="image_url" max-height="160" />
46+
</div>
47+
<div
48+
:class="{
49+
'truncate-description': true,
50+
'truncate-2': !!image_url,
51+
'truncate-10': !image_url,
52+
}"
53+
v-text="description"
54+
/>
55+
</v-card-text>
56+
<v-spacer />
57+
<v-card-actions class="flex-wrap">
58+
<v-btn
59+
text="Install"
60+
append-icon="mdi-puzzle-plus"
61+
variant="elevated"
62+
@click.stop="install"
63+
/>
64+
</v-card-actions>
65+
</v-card>
66+
<plugin-details-dialog
67+
v-if="showDialog"
68+
v-model="showDialog"
69+
v-bind="plugin"
70+
@trigger-install="install"
71+
/>
5972
</template>
6073

6174
<script>
@@ -68,9 +81,20 @@ export default {
6881
},
6982
mixins: [PluginProps],
7083
emits: ['triggerInstall'],
84+
data() {
85+
return {
86+
showDialog: false,
87+
}
88+
},
7189
methods: {
72-
install: function () {
73-
this.$emit('triggerInstall', this.plugin)
90+
openDialog: function () {
91+
this.showDialog = true
92+
},
93+
install: function (event) {
94+
this.$emit('triggerInstall', {
95+
id: this.id,
96+
version_id: event?.version_id || this.current_version_id,
97+
})
7498
},
7599
},
76100
}
@@ -93,4 +117,8 @@ export default {
93117
line-clamp: 10;
94118
-webkit-line-clamp: 10;
95119
}
120+
121+
.plugin-image-backdrop {
122+
background-color: rgb(156, 163, 175);
123+
}
96124
</style>

0 commit comments

Comments
 (0)