Skip to content
Snippets Groups Projects
Select Git revision
  • e4f762a69973f18e93204269a4281a262828b86f
  • master default protected
  • dev
  • gh-pages
4 results

system-services-metadata.md

Blame
  • default.vue 11.85 KiB
    <template>
      <v-app>
        <v-navigation-drawer v-model="drawer" fixed app>
          <v-list-item>
            <v-list-item-content>
              <v-list-item-subtitle>
                {{ version }}
              </v-list-item-subtitle>
              <v-list-item-title class="text-h6">
                Database Repository
              </v-list-item-title>
            </v-list-item-content>
          </v-list-item>
          <v-list nav>
            <v-list-item
              to="/"
              router>
              <v-list-item-action>
                <v-icon>mdi-information-outline</v-icon>
              </v-list-item-action>
              <v-list-item-content>
                <v-list-item-title>Information</v-list-item-title>
              </v-list-item-content>
            </v-list-item>
            <v-list-item
              to="/container"
              router>
              <v-list-item-action>
                <v-icon>mdi-database</v-icon>
              </v-list-item-action>
              <v-list-item-content>
                <v-list-item-title>Databases</v-list-item-title>
              </v-list-item-content>
            </v-list-item>
          </v-list>
          <div>
            <v-img
              contain
              class="tu-logo"
              :src="logo" />
          </div>
        </v-navigation-drawer>
        <v-app-bar fixed app>
          <v-app-bar-nav-icon @click.stop="drawer = !drawer" />
          <v-autocomplete
            v-model="model"
            class="ml-1"
            :items="searchResults"
            :loading="loadingSearch"
            :search-input.sync="query"
            hide-no-data
            hide-selected
            hide-details
            item-text="name"
            item-value="name"
            solo
            flat
            single-line
            label="Search ..."
            return-object>
            <template v-slot:item="data">
              <template>
                <v-list-item-icon>
                  <v-icon :title="metadata(data).text">{{ metadata(data).icon }}</v-icon>
                </v-list-item-icon>
                <v-list-item-content @click="navigate(data)">
                  <v-list-item-title class="search-result-title">{{ metadata(data).title }}</v-list-item-title>
                  <v-list-item-subtitle class="search-result-subtitle">{{ metadata(data).subtitle }}</v-list-item-subtitle>
                </v-list-item-content>
              </template>
            </template>
          </v-autocomplete>
          <v-spacer />
          <v-btn
            v-if="!token"
            class="mr-2"
            color="secondary"
            to="/login">
            <v-icon left>mdi-login</v-icon> Login
          </v-btn>
          <v-btn
            v-if="!token"
            class="mr-2"
            color="primary"
            to="/signup">
            <v-icon left>mdi-account-plus</v-icon> Signup
          </v-btn>
          <v-btn v-if="username" to="/user" plain>
            {{ username }} <sup v-if="user.email_verified">
              <v-icon color="primary" title="E-Mail verified" small>mdi-check-decagram</v-icon>
            </sup>
          </v-btn>
          <v-menu bottom offset-y left>
            <template v-slot:activator="{ on, attrs }">
              <v-btn
                icon
                v-bind="attrs"
                v-on="on">
                <v-icon>mdi-dots-vertical</v-icon>
              </v-btn>
            </template>
            <v-list>
              <v-list-item
                v-for="locale in availableLocales"
                :key="locale.code"
                :to="switchLocalePath(locale.code)">
                <v-list-item-title>{{ locale.name }}</v-list-item-title>
              </v-list-item>
              <v-list-item
                v-if="token"
                @click="logout">
                Logout
              </v-list-item>
            </v-list>
          </v-menu>
        </v-app-bar>
        <v-main>
          <v-container>
            <nuxt />
          </v-container>
        </v-main>
        <v-footer
          v-if="sandbox"
          padless>
          <v-card
            flat
            tile
            width="100%"
            class="error text-center">
            <v-card-text class="black--text">
              This is a <strong>TEST</strong> environment, do not use production/confidential data! — <a href="//github.com/fair-data-austria/dbrepo/issues/new" class="black--text">Report a bug</a>
            </v-card-text>
          </v-card>
        </v-footer>
      </v-app>
    </template>
    
    <script>
    export default {
      name: 'DefaultLayout',
      data () {
        return {
          drawer: false,
          model: null,
          query: null,
          searchResults: [],
          databases: [],
          user: {
            theme_dark: null
          },
          loadingUser: true,
          loadingSearch: false,
          loadingDatabases: false
        }
      },
      computed: {
        availableLocales () {
          return this.$i18n.locales.filter(i => i.code !== this.$i18n.locale)
        },
        token () {
          return this.$store.state.token
        },
        username () {
          return this.$store.state.user && this.$store.state.user.username
        },
        container () {
          return this.$store.state.container
        },
        db () {
          return this.$store.state.db
        },
        version () {
          return this.$config.version
        },
        config () {
          if (this.token === null) {
            return {}
          }
          return {
            headers: { Authorization: `Bearer ${this.token}` }
          }
        },
        sandbox () {
          if (this.$config.sandbox === undefined) {
            console.debug('env sandbox not found, default to', false)
            return false
          }
          console.debug('env sandbox found', this.$config.sandbox)
          return this.$config.sandbox
        },
        logo () {
          if (this.$config.logo === undefined) {
            console.debug('env logo not found, default to /logos.png')
            return '/logos.png'
          }
          console.debug('env logo found', this.$config.logo)
          return this.$config.logo
        }
      },
      watch: {
        $route () {
          this.loadDB()
          if (this.token) {
            this.loadUser()
              .then(() => this.setTheme())
          }
        },
        query (val) {
          if (!val) {
            return
          }
          setTimeout(() => {
            if (val !== this.query) {
              return
            }
            this.queryDatabases(val)
            this.queryTables(val)
            this.queryColumns(val)
          })
        }
      },
      mounted () {
        this.loadDB()
        this.loadContainers()
          .then(() => this.loadDatabases())
        this.loadUser()
          .then(() => this.setTheme())
      },
      methods: {
        metadata (item) {
          if (item.item.exchange !== undefined) {
            /* is database */
            return {
              icon: 'mdi-database',
              text: 'Database',
              link: `/container/${item.item.id}/database/${item.item.id}`,
              title: item.item.name,
              subtitle: item.item.description
            }
          }
          if (item.item.topic !== undefined) {
            /* is table */
            return {
              icon: 'mdi-table',
              text: 'Table',
              link: `/container/${item.item.tdbid}/database/${item.item.tdbid}/table/${item.item.id}`,
              title: item.item.name,
              subtitle: item.item.description
            }
          }
          if (item.item.columnType !== undefined) {
            /* is column */
            return {
              icon: 'mdi-view-column-outline',
              text: 'Column',
              link: `/container/${item.item.cdbid}/database/${item.item.cdbid}/table/${item.item.tid}`,
              title: item.item.name,
              subtitle: item.item.columnType
            }
          }
          /* is identifier */
          return {
            icon: 'mdi-lock-clock',
            text: 'Identifier',
            link: `/pid/${item.item.id}`,
            title: item.item.name,
            subtitle: item.item.description
          }
        },
        navigate (item) {
          this.$router.push(this.metadata(item).link)
        },
        logout () {
          this.$store.commit('SET_TOKEN', null)
          this.$store.commit('SET_USER', null)
          this.$toast.success('Logged out')
          this.$vuetify.theme.dark = false
          this.$router.push('/container')
        },
        async queryDatabases (v) {
          this.loadingSearch = true
          try {
            const res = await this.$axios.get(`/search/databaseindex/_search?q=isPublic:true%20AND%20*${v}*&_source_includes=id,name,description,exchange&terminate_after=10`)
            console.info('search databases results', res.data.hits.total.value)
            console.debug('search databases results', res.data.hits.hits)
            const databases = res.data.hits.hits.map(h => h._source)
            databases.forEach(d => this.searchResults.push(d))
          } catch (err) {
            console.error('Failed to load search results', err)
          }
          this.loadingSearch = false
        },
        async queryTables (v) {
          this.loadingSearch = true
          try {
            const res = await this.$axios.get(`/search/tableindex/_search?q=*${v}*&_source_includes=id,tdbid,name,description,topic&terminate_after=10`)
            console.info('search tables results', res.data.hits.total.value)
            console.debug('search tables results', res.data.hits.hits)
            const tables = res.data.hits.hits.map(h => h._source)
            tables.forEach(t => this.searchResults.push(t))
          } catch (err) {
            console.error('Failed to load search results', err)
          }
          this.loadingSearch = false
        },
        async queryColumns (v) {
          this.loadingSearch = true
          try {
            const res = await this.$axios.get(`/search/columnindex/_search?q=*${v}*&_source_includes=id,cdbid,tid,name,columnType&terminate_after=10`)
            console.info('search column results', res.data.hits.total.value)
            console.debug('search column results', res.data.hits.hits)
            const dbpubids = this.databases.filter(d => d.is_public).map(d => d.id)
            const columns = res.data.hits.hits.map(h => h._source).filter(c => dbpubids.includes(c.cdbid))
            columns.forEach(c => this.searchResults.push(c))
          } catch (err) {
            console.error('Failed to load search results', err)
          }
          this.loadingSearch = false
        },
        async loadContainers () {
          this.createDbDialog = false
          try {
            this.loadingContainers = true
            const res = await this.$axios.get('/api/container/')
            this.containers = res.data
            console.debug('containers', this.containers)
            this.error = false
          } catch (err) {
            console.error('containers', err)
            this.error = true
          }
          this.loadingContainers = false
        },
        async loadDatabases () {
          if (this.containers.length === 0) {
            return
          }
          this.loading = true
          for (const container of this.containers) {
            try {
              const res = await this.$axios.get(`/api/container/${container.id}/database`, this.config)
              for (const info of res.data) {
                info.container_id = container.id
                this.databases.push(info)
              }
            } catch (err) {
              if (err.response === undefined || err.response.status === undefined || err.response.status !== 401) {
                console.error('Failed to load databases for container', err)
              }
            }
          }
          this.loading = false
          console.debug('databases', this.databases)
        },
        async loadDB () {
          if (this.$route.params.db_id && !this.db) {
            try {
              const res = await this.$axios.get(`/api/database/${this.$route.params.db_id}`)
              this.$store.commit('SET_DATABASE', res.data)
            } catch (err) {
              console.error('Failed to load database', err)
            }
          }
        },
        async loadUser () {
          if (!this.token) {
            return
          }
          try {
            this.loadingUser = true
            const res = await this.$axios.put('/api/auth', {}, this.config)
            console.debug('user data', res.data)
            this.user = res.data
          } catch (err) {
            const { status } = err.response
            if (status === 401) {
              console.error('Token expired', err)
              this.logout()
            } else {
              console.error('user data', err)
              this.$toast.error('Failed to load user')
              this.error = true
            }
          }
          this.loadingUser = false
        },
        setTheme () {
          this.$vuetify.theme.dark = this.user.theme_dark
        }
      }
    }
    </script>
    <style>
    .search-result-title,
    .search-result-subtitle {
      overflow: hidden;
      white-space: pre-line;
    }
    .v-menu__content {
      max-width: 988px !important;
    }
    .tu-logo {
      margin: 1em 1em 0;
    }
    .univie-logo {
      margin: 1em 1em .5em;
    }
    .sl {
      padding-left: 36px;
    }
    </style>