Browse Source

Merge pull request #3035 from nocodb/refactor/gui-v2-api-snippet

pull/3076/head
Braks 2 years ago committed by GitHub
parent
commit
0292f646c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      packages/nc-gui-v2/components/monaco/Editor.vue
  2. 3
      packages/nc-gui-v2/components/smartsheet/sidebar/MenuBottom.vue
  3. 182
      packages/nc-gui-v2/components/smartsheet/sidebar/menu/ApiSnippet.vue
  4. 5
      packages/nc-gui-v2/composables/useGlobal/state.ts
  5. 5
      packages/nc-gui-v2/composables/useGlobal/types.ts
  6. 7
      packages/nc-gui-v2/composables/useViewData.ts
  7. 1
      packages/nc-gui-v2/nuxt.config.ts
  8. 932
      packages/nc-gui-v2/package-lock.json
  9. 3
      packages/nc-gui-v2/package.json
  10. 9
      packages/nc-gui-v2/plugins/state.ts

4
packages/nc-gui-v2/components/monaco/Editor.vue

@ -12,9 +12,10 @@ interface Props {
lang?: string lang?: string
validate?: boolean validate?: boolean
disableDeepCompare?: boolean disableDeepCompare?: boolean
readOnly?: boolean
} }
const { hideMinimap, lang = 'json', validate = true, disableDeepCompare = false, modelValue } = defineProps<Props>() const { hideMinimap, lang = 'json', validate = true, disableDeepCompare = false, modelValue, readOnly } = defineProps<Props>()
const emits = defineEmits(['update:modelValue']) const emits = defineEmits(['update:modelValue'])
@ -92,6 +93,7 @@ onMounted(() => {
}, },
tabSize: 2, tabSize: 2,
automaticLayout: true, automaticLayout: true,
readOnly,
minimap: { minimap: {
enabled: !hideMinimap, enabled: !hideMinimap,
}, },

3
packages/nc-gui-v2/components/smartsheet/sidebar/MenuBottom.vue

@ -12,9 +12,11 @@ const emits = defineEmits<Emits>()
const { $e } = useNuxtApp() const { $e } = useNuxtApp()
const isView = ref(false) const isView = ref(false)
let showApiSnippet = $ref(false)
function onApiSnippet() { function onApiSnippet() {
// get API snippet // get API snippet
showApiSnippet = true
$e('a:view:api-snippet') $e('a:view:api-snippet')
} }
@ -89,6 +91,7 @@ function onOpenModal(type: ViewTypes, title = '') {
</a-tooltip> </a-tooltip>
</a-menu-item> </a-menu-item>
<SmartsheetSidebarMenuApiSnippet v-model="showApiSnippet" />
<div class="flex-auto justify-end flex flex-col gap-4 mt-4"> <div class="flex-auto justify-end flex flex-col gap-4 mt-4">
<button <button
class="flex items-center gap-2 w-full mx-3 px-4 py-3 rounded !bg-primary text-white transform translate-x-4 hover:(translate-x-0 shadow-lg) transition duration-150 ease" class="flex items-center gap-2 w-full mx-3 px-4 py-3 rounded !bg-primary text-white transform translate-x-4 hover:(translate-x-0 shadow-lg) transition duration-150 ease"

182
packages/nc-gui-v2/components/smartsheet/sidebar/menu/ApiSnippet.vue

@ -0,0 +1,182 @@
<script setup lang="ts">
import HTTPSnippet from 'httpsnippet'
import { useClipboard } from '@vueuse/core'
import { notification } from 'ant-design-vue'
import { ActiveViewInj, MetaInj } from '~/context'
const props = defineProps<Props>()
const emits = defineEmits(['update:modelValue'])
interface Props {
modelValue: boolean
}
const { project } = $(useProject())
const { appInfo, token } = $(useGlobal())
const meta = $(inject(MetaInj))
const view = $(inject(ActiveViewInj))
const { xWhere } = useSmartsheetStoreOrThrow()
const { queryParams } = $(useViewData(meta, view as any, xWhere))
const { copy } = useClipboard()
let vModel = $(useVModel(props, 'modelValue', emits))
const langs = [
{
name: 'shell',
clients: ['curl', 'wget'],
},
{
name: 'javascript',
clients: ['axios', 'fetch', 'jquery', 'xhr'],
},
{
name: 'node',
clients: ['axios', 'fetch', 'request', 'native', 'unirest'],
},
{
name: 'nocodb-sdk',
clients: ['javascript', 'node'],
},
{
name: 'php',
},
{
name: 'python',
clients: ['python3', 'requests'],
},
{
name: 'ruby',
},
{
name: 'java',
},
{
name: 'c',
},
]
const selectedClient = $ref<string | undefined>(langs[0].clients && langs[0].clients[0])
const selectedLangName = $ref(langs[0].name)
const apiUrl = $computed(
() =>
new URL(`/api/v1/db/data/noco/${project.id}/${meta.title}/views/${view.title}`, (appInfo && appInfo.ncSiteUrl) || '/').href,
)
const snippet = $computed(
() =>
new HTTPSnippet({
method: 'GET',
headers: [{ name: 'xc-auth', value: token as string, comment: 'JWT Auth token' }],
url: apiUrl,
queryString: Object.entries(queryParams || {}).map(([name, value]) => {
return {
name,
value: String(value),
}
}),
}),
)
const activeLang = $computed(() => langs.find((lang) => lang.name === selectedLangName))
const code = $computed(() => {
if (activeLang?.name === 'nocodb-sdk') {
return `${selectedClient === 'node' ? 'const { Api } require("nocodb-sdk");' : 'import { Api } from "nocodb-sdk";'}
const api = new Api({
baseURL: ${JSON.stringify(apiUrl)},
headers: {
"xc-auth": ${JSON.stringify(token as string)}
}
})
api.dbViewRow.list(
"noco",
${JSON.stringify(project.title)},
${JSON.stringify(meta.title)},
${JSON.stringify(view.title)}, ${JSON.stringify(queryParams, null, 4)}).then(function (data) {
console.log(data);
}).catch(function (error) {
console.error(error);
});
`
}
return snippet.convert(activeLang?.name, selectedClient || (activeLang?.clients && activeLang?.clients[0]), {})
})
const onCopyToClipboard = () => {
copy(code)
notification.info({ message: 'Copied to clipboard' })
}
const afterVisibleChange = (visible: boolean) => {
vModel = visible
}
</script>
<template>
<a-drawer
v-model:visible="vModel"
class="h-full relative"
style="color: red"
placement="right"
size="large"
:closable="false"
@after-visible-change="afterVisibleChange"
>
<div class="flex flex-col w-full h-full">
<a-typography-title :level="4">Code Snippet</a-typography-title>
<a-tabs v-model:activeKey="selectedLangName" class="!h-full">
<a-tab-pane v-for="item in langs" :key="item.name" class="!h-full">
<template #tab>
<div class="capitalize select-none">
{{ item.name }}
</div>
</template>
<monaco-editor
class="h-[60vh] border-1 border-gray-100 py-4 rounded-sm"
:model-value="code"
:read-only="true"
lang="typescript"
:validate="false"
:disable-deep-compare="true"
/>
<div class="flex flex-row w-full justify-end space-x-3 mt-4 uppercase">
<a-button
v-t="[
'c:snippet:copy',
{ client: activeLang?.clients && (selectedClient || activeLang?.clients[0]), lang: activeLang?.name },
]"
type="primary"
@click="onCopyToClipboard"
>Copy to clipboard</a-button
>
<a-select v-if="activeLang" v-model:value="selectedClient" style="width: 6rem">
<a-select-option v-for="(client, i) in activeLang?.clients" :key="i" class="!w-full uppercase" :value="client">
{{ client }}
</a-select-option>
</a-select>
</div>
<div class="absolute bottom-4 flex flex-row justify-center w-[95%]">
<a
v-t="['e:hiring']"
class="px-4 py-2 ! rounded shadow"
href="https://angel.co/company/nocodb"
target="_blank"
@click.stop
>
🚀 We are Hiring! 🚀
</a>
</div>
</a-tab-pane>
</a-tabs>
</div>
</a-drawer>
</template>
<style scoped></style>

5
packages/nc-gui-v2/composables/useGlobal/state.ts

@ -1,7 +1,7 @@
import { usePreferredLanguages, useStorage } from '@vueuse/core' import { usePreferredLanguages, useStorage } from '@vueuse/core'
import { useJwt } from '@vueuse/integrations/useJwt' import { useJwt } from '@vueuse/integrations/useJwt'
import type { JwtPayload } from 'jwt-decode' import type { JwtPayload } from 'jwt-decode'
import type { State, StoredState } from './types' import type { AppInfo, State, StoredState } from './types'
import { computed, ref, toRefs, useCounter, useNuxtApp, useTimestamp } from '#imports' import { computed, ref, toRefs, useCounter, useNuxtApp, useTimestamp } from '#imports'
import type { User } from '~/lib' import type { User } from '~/lib'
@ -71,6 +71,8 @@ export function useGlobalState(storageKey = 'nocodb-gui-v2'): State {
set: (val) => (storage.value.token = val), set: (val) => (storage.value.token = val),
}) })
const appInfo = ref<AppInfo>({ ncSiteUrl: 'localhost:8080' })
/** reactive token payload */ /** reactive token payload */
const { payload } = useJwt<JwtPayload & User>(token) const { payload } = useJwt<JwtPayload & User>(token)
@ -88,5 +90,6 @@ export function useGlobalState(storageKey = 'nocodb-gui-v2'): State {
timestamp, timestamp,
runningRequests, runningRequests,
error, error,
appInfo,
} }
} }

5
packages/nc-gui-v2/composables/useGlobal/types.ts

@ -11,6 +11,10 @@ export interface FeedbackForm {
lastFormPollDate?: string lastFormPollDate?: string
} }
export interface AppInfo {
ncSiteUrl: string
}
export interface StoredState { export interface StoredState {
token: string | null token: string | null
user: User | null user: User | null
@ -27,6 +31,7 @@ export type State = ToRefs<Omit<StoredState, 'token'>> & {
timestamp: Ref<number> timestamp: Ref<number>
runningRequests: ReturnType<typeof useCounter> runningRequests: ReturnType<typeof useCounter>
error: Ref<any> error: Ref<any>
appInfo: Ref<AppInfo>
} }
export interface Getters { export interface Getters {

7
packages/nc-gui-v2/composables/useViewData.ts

@ -50,6 +50,12 @@ export function useViewData(
paginationData.value.totalRows = count paginationData.value.totalRows = count
} }
const queryParams = computed(() => ({
offset: (paginationData.value?.page ?? 0) - 1,
limit: paginationData.value?.pageSize ?? 25,
where: where?.value ?? '',
}))
const loadData = async (params: Parameters<Api<any>['dbViewRow']['list']>[4] = {}) => { const loadData = async (params: Parameters<Api<any>['dbViewRow']['list']>[4] = {}) => {
if (!project?.value?.id || !meta?.value?.id || !viewMeta?.value?.id) return if (!project?.value?.id || !meta?.value?.id || !viewMeta?.value?.id) return
const response = await $api.dbViewRow.list('noco', project.value.id, meta.value.id, viewMeta.value.id, { const response = await $api.dbViewRow.list('noco', project.value.id, meta.value.id, viewMeta.value.id, {
@ -246,6 +252,7 @@ export function useViewData(
return { return {
loadData, loadData,
paginationData, paginationData,
queryParams,
formattedData, formattedData,
insertRow, insertRow,
updateRowProperty, updateRowProperty,

1
packages/nc-gui-v2/nuxt.config.ts

@ -80,6 +80,7 @@ export default defineNuxtConfig({
], ],
define: { define: {
'process.env.DEBUG': 'false', 'process.env.DEBUG': 'false',
'global': {},
}, },
server: { server: {
watch: { watch: {

932
packages/nc-gui-v2/package-lock.json generated

File diff suppressed because it is too large Load Diff

3
packages/nc-gui-v2/package.json

@ -26,6 +26,8 @@
"socket.io-client": "^4.5.1", "socket.io-client": "^4.5.1",
"sortablejs": "^1.15.0", "sortablejs": "^1.15.0",
"unique-names-generator": "^4.7.1", "unique-names-generator": "^4.7.1",
"url": "^0.11.0",
"util": "^0.12.4",
"vue-i18n": "^9.1.10", "vue-i18n": "^9.1.10",
"vue-toastification": "^2.0.0-rc.5", "vue-toastification": "^2.0.0-rc.5",
"vuedraggable": "^4.1.0", "vuedraggable": "^4.1.0",
@ -59,6 +61,7 @@
"eslint-config-prettier": "^8.5.0", "eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.0.0", "eslint-plugin-prettier": "^4.0.0",
"happy-dom": "^6.0.3", "happy-dom": "^6.0.3",
"httpsnippet": "^2.0.0",
"less": "^4.1.3", "less": "^4.1.3",
"nuxt": "3.0.0-rc.4", "nuxt": "3.0.0-rc.4",
"nuxt-windicss": "^2.5.0", "nuxt-windicss": "^2.5.0",

9
packages/nc-gui-v2/plugins/state.ts

@ -13,14 +13,21 @@ import { useDark, useGlobal, watch } from '#imports'
* console.log($state.lang.value) // 'en' * console.log($state.lang.value) // 'en'
* ``` * ```
*/ */
export default defineNuxtPlugin((nuxtApp) => { export default defineNuxtPlugin(async (nuxtApp) => {
const state = useGlobal() const state = useGlobal()
const { $api } = useNuxtApp()
const darkMode = useDark() const darkMode = useDark()
/** set i18n locale to stored language */ /** set i18n locale to stored language */
nuxtApp.vueApp.i18n.locale.value = state.lang.value nuxtApp.vueApp.i18n.locale.value = state.lang.value
try {
state.appInfo.value = await $api.utils.appInfo()
} catch (e) {
console.error(e)
}
/** set current dark mode from storage */ /** set current dark mode from storage */
watch( watch(
state.darkMode, state.darkMode,

Loading…
Cancel
Save