diff --git a/src/main/client/app/app.vue b/src/main/client/app/app.vue index e03ff9b11..05ba89ef4 100644 --- a/src/main/client/app/app.vue +++ b/src/main/client/app/app.vue @@ -25,4 +25,5 @@ @import "~@/assets/css/style.css"; @import "~@/assets/css/color_2.css"; @import "~@/assets/css/responsive.css"; + @import "~@/assets/css/github-markdown.css"; diff --git a/src/main/client/app/assets/images/providers/unknown.png b/src/main/client/app/assets/images/providers/unknown.png new file mode 100644 index 000000000..e604c32a5 Binary files /dev/null and b/src/main/client/app/assets/images/providers/unknown.png differ diff --git a/src/main/client/app/main.js b/src/main/client/app/main.js index 83d3bea66..23d8513ac 100644 --- a/src/main/client/app/main.js +++ b/src/main/client/app/main.js @@ -4,6 +4,8 @@ import { Multiselect } from 'vue-multiselect'; import fontawesomeConfig from '@/shared/config/fontawesome-config'; import bootstrapVueConfig from '@/shared/config/bootstrap-vue-config'; +import initFilters from '@/shared/filters'; + import { AppDefaultLayout, AppErrorLayout, @@ -17,6 +19,9 @@ fontawesomeConfig.init(); bootstrapVueConfig.init(); Vue.use(Multiselect); +// filters +initFilters(); + // layout definitions Vue.component('app-default-layout', AppDefaultLayout); Vue.component('app-error-layout', AppErrorLayout); diff --git a/src/main/client/app/pages/modules/module-description.vue b/src/main/client/app/pages/modules/module-description.vue index e6c95c782..1e7309c1f 100644 --- a/src/main/client/app/pages/modules/module-description.vue +++ b/src/main/client/app/pages/modules/module-description.vue @@ -1,13 +1,105 @@ - + data: function data() { + return { + module: null, + moduleId: this.$route.params.moduleId, + }; + }, + + computed: { + imageUrl() { + // eslint-disable-next-line global-require, import/no-dynamic-require + return require(`@/assets/images/providers/${this.module.mainProvider}.png`); + }, + }, + + async created() { + const url = `/api/modules/${this.moduleId}`; + const response = await axios.get(url); + this.module = response.data; + }, + }; + diff --git a/src/main/client/app/pages/modules/readme.vue b/src/main/client/app/pages/modules/readme.vue new file mode 100644 index 000000000..a9fb969b1 --- /dev/null +++ b/src/main/client/app/pages/modules/readme.vue @@ -0,0 +1,45 @@ + + + diff --git a/src/main/client/app/shared/components/index.js b/src/main/client/app/shared/components/index.js index c691b82da..7a126b871 100644 --- a/src/main/client/app/shared/components/index.js +++ b/src/main/client/app/shared/components/index.js @@ -5,3 +5,4 @@ export { default as AppSideBar } from '@/shared/components/sidebar/side-bar.vue' export { default as AppBreadcrumb } from '@/shared/components/breadcrumb/breadcrumb.vue'; export { default as AppCliBadge } from '@/shared/components/cli-badge.vue'; export { default as AppFormTypeahead } from '@/shared/components/form-typeahead.vue'; +export { default as AppMarkdown } from '@/shared/components/markdown.vue'; diff --git a/src/main/client/app/shared/components/markdown.vue b/src/main/client/app/shared/components/markdown.vue new file mode 100644 index 000000000..984ad45e2 --- /dev/null +++ b/src/main/client/app/shared/components/markdown.vue @@ -0,0 +1,30 @@ + + + diff --git a/src/main/client/app/shared/config/bootstrap-vue-config.js b/src/main/client/app/shared/config/bootstrap-vue-config.js index 6699e051b..eaa70b42c 100644 --- a/src/main/client/app/shared/config/bootstrap-vue-config.js +++ b/src/main/client/app/shared/config/bootstrap-vue-config.js @@ -13,6 +13,8 @@ import { LayoutPlugin, NavbarPlugin, NavPlugin, + SpinnerPlugin, + TabsPlugin, ToastPlugin, } from 'bootstrap-vue'; import { Multiselect } from 'vue-multiselect'; @@ -31,6 +33,8 @@ export default { Vue.use(ToastPlugin); Vue.use(NavbarPlugin); Vue.use(DropdownPlugin); + Vue.use(SpinnerPlugin); + Vue.use(TabsPlugin); Vue.use(NavPlugin); Vue.use(BreadcrumbPlugin); diff --git a/src/main/client/app/shared/filters/index.js b/src/main/client/app/shared/filters/index.js new file mode 100644 index 000000000..9045c8cfd --- /dev/null +++ b/src/main/client/app/shared/filters/index.js @@ -0,0 +1,30 @@ +import Vue from 'vue'; + +function formatDate(value, options) { + if (!value) return ''; + return new Intl.DateTimeFormat(undefined, options).format(Date.parse(value)); +} + +export default function initFilters() { + + // display date like "05/04/2020, 4:20:20 AM" + Vue.filter('dateTime', (value) => formatDate(value, { + day: '2-digit', + month: '2-digit', + year: 'numeric', + hour: 'numeric', + minute: '2-digit', + second: '2-digit', + })); + + // display date like "May 04, 2020, 4:20:20 AM" + Vue.filter('dateTimeLong', (value) => formatDate(value, { + day: '2-digit', + month: 'long', + year: 'numeric', + hour: 'numeric', + minute: '2-digit', + second: '2-digit', + })); + +} diff --git a/src/main/java/io/codeka/gaia/modules/controller/ModuleRestController.java b/src/main/java/io/codeka/gaia/modules/controller/ModuleRestController.java index 485a03ecf..efe3e7022 100644 --- a/src/main/java/io/codeka/gaia/modules/controller/ModuleRestController.java +++ b/src/main/java/io/codeka/gaia/modules/controller/ModuleRestController.java @@ -1,6 +1,7 @@ package io.codeka.gaia.modules.controller; import io.codeka.gaia.modules.bo.TerraformModule; +import io.codeka.gaia.modules.repository.TerraformModuleGitRepository; import io.codeka.gaia.modules.repository.TerraformModuleRepository; import io.codeka.gaia.teams.User; import org.springframework.beans.factory.annotation.Autowired; @@ -9,8 +10,11 @@ import org.springframework.web.bind.annotation.*; import javax.validation.Valid; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; import java.time.LocalDateTime; import java.util.List; +import java.util.Optional; import java.util.UUID; /** @@ -23,9 +27,12 @@ public class ModuleRestController { private TerraformModuleRepository moduleRepository; + private TerraformModuleGitRepository moduleGitRepository; + @Autowired - public ModuleRestController(TerraformModuleRepository moduleRepository) { + public ModuleRestController(TerraformModuleRepository moduleRepository, TerraformModuleGitRepository moduleGitRepository) { this.moduleRepository = moduleRepository; + this.moduleGitRepository = moduleGitRepository; } @GetMapping @@ -69,4 +76,12 @@ public TerraformModule saveModule(@PathVariable String id, @RequestBody @Valid T return moduleRepository.save(module); } -} \ No newline at end of file + @GetMapping("/{id}/readme") + @Produces(MediaType.TEXT_PLAIN) + public Optional readme(@PathVariable String id) { + var module = moduleRepository.findById(id).orElseThrow(); + return moduleGitRepository.getReadme(module); + } + + +} diff --git a/src/main/java/io/codeka/gaia/modules/controller/ModulesMVCController.java b/src/main/java/io/codeka/gaia/modules/controller/ModulesMVCController.java index 52e6c0549..f2ba4c846 100644 --- a/src/main/java/io/codeka/gaia/modules/controller/ModulesMVCController.java +++ b/src/main/java/io/codeka/gaia/modules/controller/ModulesMVCController.java @@ -41,13 +41,6 @@ public String description(@PathVariable String id, Model model) { return "module_description"; } - @GetMapping("/modules/{id}/readme") - @Produces(MediaType.TEXT_PLAIN) - @ResponseBody - public Optional readme(@PathVariable String id) { - var module = terraformModuleRepository.findById(id).orElseThrow(); - return terraformModuleGitRepository.getReadme(module); - } } diff --git a/src/main/resources/templates/module_description.html b/src/main/resources/templates/module_description.html deleted file mode 100644 index 4649dc34b..000000000 --- a/src/main/resources/templates/module_description.html +++ /dev/null @@ -1,125 +0,0 @@ - - - - - Gaia - Module description - - - - -
- -
-
- - - - -
- -
- -
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
- -
-
-
- - - - - -
-
-
-
-
-
- -
- - - - - \ No newline at end of file diff --git a/src/main/resources/templates/vue_templates/components/markdown.vue b/src/main/resources/templates/vue_templates/components/markdown.vue deleted file mode 100644 index 32b28a6c0..000000000 --- a/src/main/resources/templates/vue_templates/components/markdown.vue +++ /dev/null @@ -1,20 +0,0 @@ - - - \ No newline at end of file diff --git a/src/main/resources/templates/vue_templates/components/readme.vue b/src/main/resources/templates/vue_templates/components/readme.vue deleted file mode 100644 index 748b4637f..000000000 --- a/src/main/resources/templates/vue_templates/components/readme.vue +++ /dev/null @@ -1,26 +0,0 @@ - - - \ No newline at end of file diff --git a/src/main/resources/templates/vue_templates/filters/date-time.vue b/src/main/resources/templates/vue_templates/filters/date-time.vue deleted file mode 100644 index 20a2343be..000000000 --- a/src/main/resources/templates/vue_templates/filters/date-time.vue +++ /dev/null @@ -1,29 +0,0 @@ - \ No newline at end of file diff --git a/src/test/java/io/codeka/gaia/modules/controller/ModuleRestControllerTest.java b/src/test/java/io/codeka/gaia/modules/controller/ModuleRestControllerTest.java index 779422998..aface176f 100644 --- a/src/test/java/io/codeka/gaia/modules/controller/ModuleRestControllerTest.java +++ b/src/test/java/io/codeka/gaia/modules/controller/ModuleRestControllerTest.java @@ -1,6 +1,8 @@ package io.codeka.gaia.modules.controller; +import io.codeka.gaia.modules.bo.ModuleMetadata; import io.codeka.gaia.modules.bo.TerraformModule; +import io.codeka.gaia.modules.repository.TerraformModuleGitRepository; import io.codeka.gaia.modules.repository.TerraformModuleRepository; import io.codeka.gaia.teams.Team; import io.codeka.gaia.teams.User; @@ -13,6 +15,7 @@ import java.time.LocalDateTime; import java.util.List; +import java.util.NoSuchElementException; import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; @@ -28,6 +31,9 @@ class ModuleRestControllerTest { @Mock private TerraformModuleRepository moduleRepository; + @Mock + private TerraformModuleGitRepository moduleGitRepository; + private User admin; private User bob; @@ -210,4 +216,32 @@ void updateModule_shouldSetUpdatedMetadata(){ assertThat(module.getModuleMetadata().getUpdatedBy()).isEqualTo(bob); } -} \ No newline at end of file + @Test + void readme_shouldReturnReadmeContent() { + // given + var module = new TerraformModule(); + var readme = "README..."; + + // when + when(moduleRepository.findById(anyString())).thenReturn(Optional.of(module)); + when(moduleGitRepository.getReadme(module)).thenReturn(Optional.of(readme)); + var result = moduleRestController.readme("TEST"); + + // then + assertThat(result).isPresent().get().isEqualTo(readme); + verify(moduleRepository).findById("TEST"); + verify(moduleGitRepository).getReadme(module); + } + + @Test + void readme_shouldThrowExceptionIfModuleNotFound() { + // when + when(moduleRepository.findById(anyString())).thenReturn(Optional.empty()); + assertThrows(NoSuchElementException.class, () -> moduleRestController.readme("TEST")); + + // then + verify(moduleRepository).findById("TEST"); + verifyNoInteractions(moduleGitRepository); + } + +} diff --git a/src/test/java/io/codeka/gaia/modules/controller/ModulesMVCControllerTest.java b/src/test/java/io/codeka/gaia/modules/controller/ModulesMVCControllerTest.java index 63f6942d6..a373f2ec9 100644 --- a/src/test/java/io/codeka/gaia/modules/controller/ModulesMVCControllerTest.java +++ b/src/test/java/io/codeka/gaia/modules/controller/ModulesMVCControllerTest.java @@ -73,34 +73,6 @@ void description_shouldThrowExceptionIfModuleNotFound() { verify(model, never()).addAttribute(eq("module"), any()); } - @Test - void readme_shouldReturnReadmeContent() { - // given - var module = new TerraformModule(); - var readme = "README..."; - - // when - when(moduleRepository.findById(anyString())).thenReturn(Optional.of(module)); - when(moduleGitRepository.getReadme(module)).thenReturn(Optional.of(readme)); - var result = controller.readme("TEST"); - - // then - assertThat(result).isPresent().get().isEqualTo(readme); - verify(moduleRepository).findById("TEST"); - verify(moduleGitRepository).getReadme(module); - } - - @Test - void readme_shouldThrowExceptionIfModuleNotFound() { - // when - when(moduleRepository.findById(anyString())).thenReturn(Optional.empty()); - assertThrows(NoSuchElementException.class, () -> controller.readme("TEST")); - - // then - verify(moduleRepository).findById("TEST"); - verifyNoInteractions(moduleGitRepository); - } - @Test void importModule_shouldShowImportModuleView(){ // when