Skip to content
This repository was archived by the owner on Sep 26, 2020. It is now read-only.

Commit 3606852

Browse files
authored
Add plugin modification and make plugins unique (#151)
1 parent 423bbd8 commit 3606852

File tree

9 files changed

+112
-43
lines changed

9 files changed

+112
-43
lines changed

aws/src/main/kotlin/edu/wpi/axon/aws/plugin/S3PluginManager.kt

+8-3
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import software.amazon.awssdk.services.s3.model.NoSuchKeyException
2020
class S3PluginManager(
2121
private val s3Manager: S3Manager,
2222
private val cacheName: String,
23-
private val officialPlugins: List<Plugin.Official>
23+
private val officialPlugins: Set<Plugin.Official>
2424
) : PluginManager {
2525

2626
private lateinit var localPluginManager: LocalPluginManager
@@ -44,8 +44,13 @@ class S3PluginManager(
4444
s3Manager.uploadPluginCache(cacheName, cacheFile)
4545
}
4646

47-
override fun removeUnofficialPlugin(plugin: Plugin.Unofficial) {
48-
localPluginManager.removeUnofficialPlugin(plugin)
47+
override fun removeUnofficialPlugin(pluginName: String) {
48+
localPluginManager.removeUnofficialPlugin(pluginName)
49+
s3Manager.uploadPluginCache(cacheName, cacheFile)
50+
}
51+
52+
override fun modifyUnofficialPlugin(pluginName: String, plugin: Plugin.Unofficial) {
53+
localPluginManager.modifyUnofficialPlugin(pluginName, plugin)
4954
s3Manager.uploadPluginCache(cacheName, cacheFile)
5055
}
5156

aws/src/test/kotlin/edu/wpi/axon/aws/plugin/S3PluginManagerTest.kt

+32-6
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ internal class S3PluginManagerTest {
1818
val pluginCacheFile = File(tempDir, "cache.json").apply { createNewFile() }
1919
val plugin1 = Plugin.Unofficial("a", "a")
2020
val plugin2 = Plugin.Unofficial("b", "b")
21-
pluginCacheFile.writeText(PluginCache(listOf(plugin1, plugin2)).serialize())
21+
pluginCacheFile.writeText(PluginCache(setOf(plugin1, plugin2)).serialize())
2222

2323
val s3Manager = mockk<S3Manager> {
2424
every { downloadPluginCache(any()) } returns pluginCacheFile
@@ -27,7 +27,7 @@ internal class S3PluginManagerTest {
2727
val manager = S3PluginManager(
2828
s3Manager,
2929
"plugin-cache",
30-
listOf()
30+
setOf()
3131
)
3232
manager.initialize()
3333
manager.listPlugins().shouldContainExactlyInAnyOrder(plugin1, plugin2)
@@ -47,7 +47,7 @@ internal class S3PluginManagerTest {
4747
val manager = S3PluginManager(
4848
s3Manager,
4949
"plugin-cache",
50-
listOf()
50+
setOf()
5151
)
5252
manager.initialize()
5353
manager.addUnofficialPlugin(plugin1)
@@ -62,7 +62,7 @@ internal class S3PluginManagerTest {
6262
val pluginCacheFile = File(tempDir, "cache.json").apply { createNewFile() }
6363
val plugin1 = Plugin.Unofficial("a", "a")
6464
val plugin2 = Plugin.Unofficial("b", "b")
65-
pluginCacheFile.writeText(PluginCache(listOf(plugin1, plugin2)).serialize())
65+
pluginCacheFile.writeText(PluginCache(setOf(plugin1, plugin2)).serialize())
6666

6767
val s3Manager = mockk<S3Manager> {
6868
every { downloadPluginCache(any()) } returns pluginCacheFile
@@ -72,13 +72,39 @@ internal class S3PluginManagerTest {
7272
val manager = S3PluginManager(
7373
s3Manager,
7474
"plugin-cache",
75-
listOf()
75+
setOf()
7676
)
7777
manager.initialize()
78-
manager.removeUnofficialPlugin(plugin1)
78+
manager.removeUnofficialPlugin(plugin1.name)
7979
manager.listPlugins().shouldContainExactlyInAnyOrder(plugin2)
8080

8181
verify(exactly = 1) { s3Manager.downloadPluginCache("plugin-cache") }
8282
verify(exactly = 1) { s3Manager.uploadPluginCache("plugin-cache", pluginCacheFile) }
8383
}
84+
85+
@Test
86+
fun `modifying a plugin modifies it in s3`(@TempDir tempDir: File) {
87+
val pluginCacheFile = File(tempDir, "cache.json").apply { createNewFile() }
88+
val plugin1 = Plugin.Unofficial("a", "a")
89+
val plugin2 = Plugin.Unofficial("b", "b")
90+
val newPlugin1 = Plugin.Unofficial("a", "a1")
91+
pluginCacheFile.writeText(PluginCache(setOf(plugin1, plugin2)).serialize())
92+
93+
val s3Manager = mockk<S3Manager> {
94+
every { downloadPluginCache(any()) } returns pluginCacheFile
95+
every { uploadPluginCache(any(), any()) } returns Unit
96+
}
97+
98+
val manager = S3PluginManager(
99+
s3Manager,
100+
"plugin-cache",
101+
setOf()
102+
)
103+
manager.initialize()
104+
manager.modifyUnofficialPlugin(plugin1.name, newPlugin1)
105+
manager.listPlugins().shouldContainExactlyInAnyOrder(newPlugin1, plugin2)
106+
107+
verify(exactly = 1) { s3Manager.downloadPluginCache("plugin-cache") }
108+
verify(exactly = 1) { s3Manager.uploadPluginCache("plugin-cache", pluginCacheFile) }
109+
}
84110
}

plugin/src/main/kotlin/edu/wpi/axon/plugin/LocalPluginManager.kt

+13-6
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ import mu.KotlinLogging
1313
*/
1414
class LocalPluginManager(
1515
private val pluginCacheFile: File,
16-
private val officialPlugins: List<Plugin.Official>
16+
private val officialPlugins: Set<Plugin.Official>
1717
) : PluginManager {
1818

1919
private var initialized = false
20-
private val unofficialPlugins = mutableListOf<Plugin.Unofficial>()
20+
private val unofficialPlugins = mutableSetOf<Plugin.Unofficial>()
2121

2222
override fun initialize() {
2323
if (!pluginCacheFile.exists()) {
@@ -28,7 +28,7 @@ class LocalPluginManager(
2828
PluginCache.deserialize(pluginCacheFile.readText())
2929
} catch (ex: JsonDecodingException) {
3030
LOGGER.warn(ex) { "Invalid plugin cache file contents. Creating new cache." }
31-
val newCache = PluginCache(listOf())
31+
val newCache = PluginCache(setOf())
3232
pluginCacheFile.writeText(newCache.serialize())
3333
newCache
3434
}
@@ -37,7 +37,7 @@ class LocalPluginManager(
3737
initialized = true
3838
}
3939

40-
override fun listPlugins(): List<Plugin> {
40+
override fun listPlugins(): Set<Plugin> {
4141
check(initialized)
4242
return officialPlugins + unofficialPlugins
4343
}
@@ -48,9 +48,16 @@ class LocalPluginManager(
4848
synchronizeCacheFile()
4949
}
5050

51-
override fun removeUnofficialPlugin(plugin: Plugin.Unofficial) {
51+
override fun removeUnofficialPlugin(pluginName: String) {
5252
check(initialized)
53-
unofficialPlugins.remove(plugin)
53+
unofficialPlugins.remove(unofficialPlugins.first { it.name == pluginName })
54+
synchronizeCacheFile()
55+
}
56+
57+
override fun modifyUnofficialPlugin(pluginName: String, plugin: Plugin.Unofficial) {
58+
check(initialized)
59+
unofficialPlugins.remove(unofficialPlugins.first { it.name == pluginName })
60+
unofficialPlugins.add(plugin)
5461
synchronizeCacheFile()
5562
}
5663

plugin/src/main/kotlin/edu/wpi/axon/plugin/Plugin.kt

+8-2
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,19 @@ sealed class Plugin {
2424
* An Axon-supported plugin that users get by default.
2525
*/
2626
@Serializable
27-
data class Official(override val name: String, override val contents: String) : Plugin()
27+
data class Official(
28+
override val name: String,
29+
override val contents: String
30+
) : Plugin()
2831

2932
/**
3033
* A plugin that the user added themselves.
3134
*/
3235
@Serializable
33-
data class Unofficial(override val name: String, override val contents: String) : Plugin()
36+
data class Unofficial(
37+
override val name: String,
38+
override val contents: String
39+
) : Plugin()
3440

3541
fun serialize(): String = Json(
3642
JsonConfiguration.Stable

plugin/src/main/kotlin/edu/wpi/axon/plugin/PluginCache.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import kotlinx.serialization.json.JsonConfiguration
1111
*/
1212
@Serializable
1313
data class PluginCache(
14-
val plugins: List<Plugin.Unofficial>
14+
val plugins: Set<Plugin.Unofficial>
1515
) {
1616
fun serialize(): String = Json(
1717
JsonConfiguration.Stable

plugin/src/main/kotlin/edu/wpi/axon/plugin/PluginManager.kt

+11-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ interface PluginManager {
1616
*
1717
* @return All plugins.
1818
*/
19-
fun listPlugins(): List<Plugin>
19+
fun listPlugins(): Set<Plugin>
2020

2121
/**
2222
* Adds a plugin.
@@ -28,7 +28,15 @@ interface PluginManager {
2828
/**
2929
* Removes a plugin.
3030
*
31-
* @param plugin The plugin to remove.
31+
* @param pluginName The name of the plugin to remove.
3232
*/
33-
fun removeUnofficialPlugin(plugin: Plugin.Unofficial)
33+
fun removeUnofficialPlugin(pluginName: String)
34+
35+
/**
36+
* Modified a plugin.
37+
*
38+
* @param pluginName The name of the plugin to modify.
39+
* @param plugin The new plugin value.
40+
*/
41+
fun modifyUnofficialPlugin(pluginName: String, plugin: Plugin.Unofficial)
3442
}

plugin/src/test/kotlin/edu/wpi/axon/plugin/LocalPluginManagerTest.kt

+33-15
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package edu.wpi.axon.plugin
22

33
import io.kotlintest.matchers.collections.shouldBeEmpty
4+
import io.kotlintest.matchers.collections.shouldContainExactly
45
import io.kotlintest.matchers.collections.shouldContainExactlyInAnyOrder
56
import io.kotlintest.shouldThrow
67
import java.io.File
@@ -13,7 +14,7 @@ internal class LocalPluginManagerTest {
1314
@Test
1415
fun `list plugins with just official plugins`(@TempDir tempDir: File) {
1516
val pluginCacheFile = File(tempDir, "cache.json").apply { createNewFile() }
16-
val officialPlugins = listOf(
17+
val officialPlugins = setOf(
1718
Plugin.Official("a", "a"),
1819
Plugin.Official("b", "b")
1920
)
@@ -26,9 +27,9 @@ internal class LocalPluginManagerTest {
2627
@Test
2728
fun `list plugins with just unofficial plugins`(@TempDir tempDir: File) {
2829
val pluginCacheFile = File(tempDir, "cache.json").apply { createNewFile() }
29-
val manager = LocalPluginManager(pluginCacheFile, listOf())
30+
val manager = LocalPluginManager(pluginCacheFile, setOf())
3031
manager.initialize()
31-
val unofficialPlugins = listOf(
32+
val unofficialPlugins = setOf(
3233
Plugin.Unofficial("a", "a"),
3334
Plugin.Unofficial("b", "b")
3435
)
@@ -39,12 +40,12 @@ internal class LocalPluginManagerTest {
3940
@Test
4041
fun `list plugins with official and unofficial plugins`(@TempDir tempDir: File) {
4142
val pluginCacheFile = File(tempDir, "cache.json").apply { createNewFile() }
42-
val officialPlugins = listOf(
43+
val officialPlugins = setOf(
4344
Plugin.Official("a", "a"),
4445
Plugin.Official("b", "b")
4546
)
4647

47-
val unofficialPlugins = listOf(
48+
val unofficialPlugins = setOf(
4849
Plugin.Unofficial("a", "a"),
4950
Plugin.Unofficial("b", "b")
5051
)
@@ -58,22 +59,22 @@ internal class LocalPluginManagerTest {
5859
@Test
5960
fun `list plugins with calling initialize`(@TempDir tempDir: File) {
6061
File(tempDir, "cache.json").apply { createNewFile() }
61-
shouldThrow<IllegalStateException> { LocalPluginManager(tempDir, listOf()).listPlugins() }
62+
shouldThrow<IllegalStateException> { LocalPluginManager(tempDir, setOf()).listPlugins() }
6263
}
6364

6465
@Test
6566
fun `load plugins from local cache`(@TempDir tempDir: File) {
6667
val pluginCacheFile = File(tempDir, "cache.json").apply { createNewFile() }
6768
val exampleCacheData = PluginCache(
68-
listOf(
69+
setOf(
6970
Plugin.Unofficial("a", "a"),
7071
Plugin.Unofficial("b", "b")
7172
)
7273
)
7374

7475
pluginCacheFile.writeText(exampleCacheData.serialize())
7576

76-
val manager = LocalPluginManager(pluginCacheFile, listOf())
77+
val manager = LocalPluginManager(pluginCacheFile, setOf())
7778
manager.initialize()
7879
manager.listPlugins().shouldContainExactlyInAnyOrder(exampleCacheData.plugins)
7980
}
@@ -84,10 +85,10 @@ internal class LocalPluginManagerTest {
8485
val plugin1 = Plugin.Unofficial("a", "a")
8586
val plugin2 = Plugin.Unofficial("b", "b")
8687

87-
val exampleCacheData = PluginCache(listOf(plugin1))
88+
val exampleCacheData = PluginCache(setOf(plugin1))
8889
pluginCacheFile.writeText(exampleCacheData.serialize())
8990

90-
val manager = LocalPluginManager(pluginCacheFile, listOf())
91+
val manager = LocalPluginManager(pluginCacheFile, setOf())
9192
manager.initialize()
9293
manager.addUnofficialPlugin(plugin2)
9394

@@ -101,23 +102,40 @@ internal class LocalPluginManagerTest {
101102
val plugin1 = Plugin.Unofficial("a", "a")
102103
val plugin2 = Plugin.Unofficial("b", "b")
103104

104-
val exampleCacheData = PluginCache(listOf(plugin1, plugin2))
105+
val exampleCacheData = PluginCache(setOf(plugin1, plugin2))
105106
pluginCacheFile.writeText(exampleCacheData.serialize())
106107

107-
val manager = LocalPluginManager(pluginCacheFile, listOf())
108+
val manager = LocalPluginManager(pluginCacheFile, setOf())
108109
manager.initialize()
109-
manager.removeUnofficialPlugin(plugin1)
110+
manager.removeUnofficialPlugin(plugin1.name)
110111
PluginCache.deserialize(pluginCacheFile.readText()).plugins
111-
.shouldContainExactlyInAnyOrder(plugin2)
112+
.shouldContainExactly(plugin2)
112113
}
113114

114115
@Test
115116
fun `initialize with an invalid cache file`(@TempDir tempDir: File) {
116117
val pluginCacheFile = File(tempDir, "cache.json").apply { createNewFile() }
117118
pluginCacheFile.writeText(RandomStringUtils.randomAlphanumeric(10))
118119

119-
val manager = LocalPluginManager(pluginCacheFile, listOf())
120+
val manager = LocalPluginManager(pluginCacheFile, setOf())
120121
manager.initialize()
121122
manager.listPlugins().shouldBeEmpty()
122123
}
124+
125+
@Test
126+
fun `modifying a plugin modifies it in the cache`(@TempDir tempDir: File) {
127+
val pluginCacheFile = File(tempDir, "cache.json").apply { createNewFile() }
128+
val plugin1 = Plugin.Unofficial("a", "a")
129+
val plugin2 = Plugin.Unofficial("b", "b")
130+
131+
val exampleCacheData = PluginCache(setOf(plugin1, plugin2))
132+
pluginCacheFile.writeText(exampleCacheData.serialize())
133+
134+
val manager = LocalPluginManager(pluginCacheFile, setOf())
135+
manager.initialize()
136+
val newPlugin1 = Plugin.Unofficial("a", "a1")
137+
manager.modifyUnofficialPlugin(plugin1.name, newPlugin1)
138+
PluginCache.deserialize(pluginCacheFile.readText()).plugins
139+
.shouldContainExactlyInAnyOrder(newPlugin1, plugin2)
140+
}
123141
}

ui-vaadin/src/main/kotlin/edu/wpi/axon/ui/FrontendKoinModules.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ fun defaultFrontendModule() = module {
5656

5757
single(named(datasetPluginManagerName)) {
5858
// TODO: Load official plugins from resources
59-
val officialPlugins = listOf(
59+
val officialPlugins = setOf(
6060
datasetPassthroughPlugin,
6161
processMnistTypePlugin
6262
)
@@ -84,7 +84,7 @@ fun defaultFrontendModule() = module {
8484

8585
single(named(testPluginManagerName)) {
8686
// TODO: Load official plugins from resources
87-
val officialPlugins = listOf(
87+
val officialPlugins = setOf(
8888
Plugin.Official("Test test plugin", """print("Hello from test test plugin!")""")
8989
)
9090

ui-vaadin/src/main/kotlin/edu/wpi/axon/ui/view/jobs/JobGrid.kt

+4-5
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,7 @@ class JobGrid : Grid<Job>() {
3838
fun (@VaadinDsl HasComponents).jobGrid(
3939
dataProvider: DataProvider<Job, *>? = null,
4040
block: (@VaadinDsl JobGrid).() -> Unit = {}
41-
) =
42-
init(JobGrid()) {
43-
if (dataProvider != null) this.dataProvider = dataProvider
44-
block()
45-
}
41+
) = init(JobGrid()) {
42+
if (dataProvider != null) this.dataProvider = dataProvider
43+
block()
44+
}

0 commit comments

Comments
 (0)