mirror of
https://github.com/OpenBankProject/API-Explorer-II.git
synced 2026-02-06 10:47:04 +00:00
Merge remote-tracking branch 'UPSTREAM/develop' into UPSTREAM-develop
# Conflicts: # .gitignore # yarn.lock
This commit is contained in:
commit
ec01a21078
@ -5,3 +5,10 @@ VITE_OBP_EXPLORER_HOST=http://localhost:5173
|
||||
VITE_OBP_CONSUMER_KEY=your_consumer_key
|
||||
VITE_OBP_CONSUMER_SECRET=your_consumer_secret
|
||||
VITE_OBP_REDIRECT_URL=http://localhost:5173/api/callback
|
||||
VITE_OPB_SERVER_SESSION_PASSWORD=very secret
|
||||
# Product styling setting
|
||||
#VITE_OBP_LINKS_COLOR="#52b165"
|
||||
#VITE_OBP_HEADER_LINKS_COLOR="#39455f"
|
||||
#VITE_OBP_HEADER_LINKS_HOVER_COLOR="#39455f"
|
||||
#VITE_OBP_HEADER_LINKS_BACKGROUND_COLOR="#eef0f4"
|
||||
#VITE_OBP_LOGO_URL=https://static.openbankproject.com/images/obp_logo.png
|
||||
|
||||
13
.gitignore
vendored
13
.gitignore
vendored
@ -7,7 +7,6 @@ yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
*.lock
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
@ -29,4 +28,14 @@ coverage
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
.fleet
|
||||
|
||||
#files from npm-run-build
|
||||
package-lock.json
|
||||
tsconfig.app.tsbuildinfo
|
||||
tsconfig.vitest.tsbuildinfo
|
||||
tsconfig.node.tsbuildinfo
|
||||
vite.config.d.ts
|
||||
vite.config.js
|
||||
vitest.config.d.ts
|
||||
vitest.config.js
|
||||
components.d.ts
|
||||
52
README.md
52
README.md
@ -5,6 +5,8 @@ Welcome to the OBP API Explorer II
|
||||
|
||||
This application is used to explore OBP APIs and interact with the data and services in the context of the logged in user.
|
||||
|
||||
This application will gradually replace the original API Explorer. Long live the API Explorer!
|
||||
|
||||
|
||||
|
||||
## Install the Prerequisite Software
|
||||
@ -13,7 +15,7 @@ This application is used to explore OBP APIs and interact with the data and serv
|
||||
|
||||
### Development Project Setup
|
||||
|
||||
* Setup your .env (see .env.example)
|
||||
* Setup your .env file (see .env.example)
|
||||
|
||||
##### Install dependencies
|
||||
|
||||
@ -52,52 +54,17 @@ npm test:unit
|
||||
```
|
||||
</strike>
|
||||
|
||||
|
||||
##### Lint with [ESLint](https://eslint.org/)
|
||||
|
||||
```sh
|
||||
yarn lint
|
||||
```
|
||||
or
|
||||
```sh
|
||||
npm lint
|
||||
```
|
||||
|
||||
##### Format with [Prettier](https://prettier.io/)
|
||||
|
||||
```sh
|
||||
yarn format
|
||||
```
|
||||
or
|
||||
```sh
|
||||
npm format
|
||||
```
|
||||
|
||||
## Compile and Minify for Production
|
||||
|
||||
##### Build the frontend
|
||||
##### Build
|
||||
|
||||
```sh
|
||||
yarn build
|
||||
```
|
||||
or
|
||||
```sh
|
||||
npm build
|
||||
```
|
||||
|
||||
##### Build the backend
|
||||
|
||||
```sh
|
||||
yarn build-server
|
||||
```
|
||||
or
|
||||
```sh
|
||||
npm build-server
|
||||
npm run build
|
||||
```
|
||||
|
||||
##### Start the backend server
|
||||
```sh
|
||||
your-absolute-path-to-server-dist> node app.js
|
||||
npx ts-node <path-to-your-install>/server/app.ts
|
||||
```
|
||||
|
||||
##### Nginx deployment
|
||||
@ -106,7 +73,7 @@ your-absolute-path-to-server-dist> node app.js
|
||||
server {
|
||||
# Frontend
|
||||
location / {
|
||||
root /your_absolute_path_to_dist/dist;
|
||||
root /path_to_dist/dist;
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
@ -117,11 +84,6 @@ server {
|
||||
}
|
||||
```
|
||||
|
||||
```sh
|
||||
nginx -s reload //Restart your nginx
|
||||
```
|
||||
|
||||
|
||||
# LICENSE
|
||||
|
||||
This project is licensed under the AGPL V3 (see NOTICE) and a commercial license from TESOBE.
|
||||
|
||||
7
components.d.ts
vendored
7
components.d.ts
vendored
@ -14,10 +14,13 @@ declare module '@vue/runtime-core' {
|
||||
ElAside: typeof import('element-plus/es')['ElAside']
|
||||
ElBacktop: typeof import('element-plus/es')['ElBacktop']
|
||||
ElButton: typeof import('element-plus/es')['ElButton']
|
||||
ElCard: typeof import('element-plus/es')['ElCard']
|
||||
ElCol: typeof import('element-plus/es')['ElCol']
|
||||
ElCollapse: typeof import('element-plus/es')['ElCollapse']
|
||||
ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
|
||||
ElContainer: typeof import('element-plus/es')['ElContainer']
|
||||
ElDescriptions: typeof import('element-plus/es')['ElDescriptions']
|
||||
ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem']
|
||||
ElDivider: typeof import('element-plus/es')['ElDivider']
|
||||
ElDropdown: typeof import('element-plus/es')['ElDropdown']
|
||||
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
|
||||
@ -30,9 +33,13 @@ declare module '@vue/runtime-core' {
|
||||
ElInput: typeof import('element-plus/es')['ElInput']
|
||||
ElMain: typeof import('element-plus/es')['ElMain']
|
||||
ElRow: typeof import('element-plus/es')['ElRow']
|
||||
ElTable: typeof import('element-plus/es')['ElTable']
|
||||
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
|
||||
ElTag: typeof import('element-plus/es')['ElTag']
|
||||
GlossarySearchNav: typeof import('./src/components/GlossarySearchNav.vue')['default']
|
||||
HeaderNav: typeof import('./src/components/HeaderNav.vue')['default']
|
||||
Menu: typeof import('./src/components/Menu.vue')['default']
|
||||
MessageDocsSearchNav: typeof import('./src/components/MessageDocsSearchNav.vue')['default']
|
||||
Preview: typeof import('./src/components/Preview.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
|
||||
71
index.html
71
index.html
@ -1,17 +1,76 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>API Explorer</title>
|
||||
<link rel="stylesheet" href="/styles/default.min.css">
|
||||
<link rel="stylesheet" href="/styles/androidstudio.min.css">
|
||||
<link rel="stylesheet" href="/styles/default.min.css" />
|
||||
<link rel="stylesheet" href="/styles/default.min.css" />
|
||||
<link rel="stylesheet" href="/styles/androidstudio.min.css" />
|
||||
<script src="/js/highlight.min.js"></script>
|
||||
<script src="/js/highlightjs-line-numbers.min.js"></script>
|
||||
<style>
|
||||
.loading-page {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.messages {
|
||||
width: 80%;
|
||||
height: 100px;
|
||||
background-color: #e3e3e3;
|
||||
margin: 100px auto;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.progress-info {
|
||||
width: 50%;
|
||||
height: 100px;
|
||||
padding: 5px;
|
||||
margin: 50px auto;
|
||||
text-align: center;
|
||||
font-family: 'Roboto';
|
||||
}
|
||||
|
||||
.lds-dual-ring {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
.lds-dual-ring:after {
|
||||
content: ' ';
|
||||
display: block;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
margin: 8px;
|
||||
border-radius: 50%;
|
||||
border: 6px solid #50b164;
|
||||
border-color: #50b164 transparent #50b164 transparent;
|
||||
animation: lds-dual-ring 1.2s linear infinite;
|
||||
}
|
||||
@keyframes lds-dual-ring {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<div id="app">
|
||||
<div class="loading-page" id="loading-api-spinner">
|
||||
<img src="/src/assets/logo2x-1.png" style="width: 304px; margin-top: 50px" />
|
||||
<div class="lds-dual-ring"></div>
|
||||
<div class="progress-info" id="loading-api-info"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "api-explorer",
|
||||
"version": "0.0.0",
|
||||
"version": "1.0.14",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite & ts-node server/app.ts",
|
||||
@ -25,7 +25,7 @@
|
||||
"express-session": "^1.17.3",
|
||||
"highlight.js": "^11.7.0",
|
||||
"oauth": "^0.10.0",
|
||||
"obp-typescript": "^1.0.34",
|
||||
"obp-typescript": "^1.0.36",
|
||||
"pinia": "^2.0.32",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"routing-controllers": "^0.10.3",
|
||||
@ -49,6 +49,7 @@
|
||||
"jsdom": "^21.1.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^2.8.4",
|
||||
"superagent": "^8.1.2",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "~4.8.4",
|
||||
"unplugin-auto-import": "^0.15.1",
|
||||
|
||||
3
public/js/worker/web-worker.js
Normal file
3
public/js/worker/web-worker.js
Normal file
@ -0,0 +1,3 @@
|
||||
onmessage = (event) => {
|
||||
postMessage(event.data);
|
||||
};
|
||||
@ -8,25 +8,40 @@ import path from 'path'
|
||||
|
||||
const port = 8085
|
||||
const app: Application = express()
|
||||
const host = process.env.VITE_OBP_EXPLORER_HOST
|
||||
const httpsOrNot = host ? host.indexOf("https://") == 0 ? true : false : true
|
||||
|
||||
app.use(express.json())
|
||||
let sessionObject = {
|
||||
secret: process.env.VITE_OPB_SERVER_SESSION_PASSWORD,
|
||||
resave: false,
|
||||
saveUninitialized: true,
|
||||
cookie: {
|
||||
httpOnly: true,
|
||||
secure: false,
|
||||
maxAge: 300*1000, // 5 minutes in milliseconds
|
||||
}
|
||||
}
|
||||
if (app.get('env') === 'production') {
|
||||
app.set('trust proxy', 1) // trust first proxy
|
||||
sessionObject.cookie.secure = true // serve secure cookies
|
||||
}
|
||||
app.use(
|
||||
session({
|
||||
secret: 'very secret',
|
||||
resave: false,
|
||||
saveUninitialized: true
|
||||
})
|
||||
session(sessionObject)
|
||||
)
|
||||
useContainer(Container)
|
||||
|
||||
const routePrefix = '/api'
|
||||
|
||||
const server = useExpressServer(app, {
|
||||
//routePrefix: '/api/v1',
|
||||
routePrefix: '/api',
|
||||
routePrefix: routePrefix,
|
||||
controllers: [path.join(__dirname + '/controllers/*.ts')],
|
||||
middlewares: [path.join(__dirname + '/middlewares/*.ts')]
|
||||
})
|
||||
|
||||
export const instance = server.listen(port)
|
||||
|
||||
console.log('Server running at http://localhost:' + port)
|
||||
console.log(`Backend is running. You can check a status at http://localhost:${port}${routePrefix}/status`)
|
||||
|
||||
export default app
|
||||
|
||||
95
server/controllers/StatusController.ts
Normal file
95
server/controllers/StatusController.ts
Normal file
@ -0,0 +1,95 @@
|
||||
import { Controller, Session, Req, Res, Get } from 'routing-controllers'
|
||||
import { Request, Response } from 'express'
|
||||
import OBPClientService from '../services/OBPClientService'
|
||||
import OauthInjectedService from '../services/OauthInjectedService'
|
||||
import { Service } from 'typedi'
|
||||
import { OAuthConfig } from 'obp-typescript'
|
||||
|
||||
@Service()
|
||||
@Controller('/status')
|
||||
export class StatusController {
|
||||
private obpExplorerHome = process.env.VITE_OBP_EXPLORER_HOST
|
||||
private connectors = [
|
||||
'kafka_vSept2018',
|
||||
'akka_vDec2018',
|
||||
'rest_vMar2019',
|
||||
'stored_procedure_vDec2019'
|
||||
]
|
||||
constructor(
|
||||
private obpClientService: OBPClientService,
|
||||
private oauthInjectedService: OauthInjectedService
|
||||
) {}
|
||||
@Get('/')
|
||||
async index(
|
||||
@Session() session: any,
|
||||
@Req() request: Request,
|
||||
@Res() response: Response
|
||||
): Response {
|
||||
const oauthConfig = session['clientConfig']
|
||||
const version = this.obpClientService.getOBPVersion()
|
||||
const apiVersions = await this.checkApiVersions(oauthConfig, version)
|
||||
const messageDocs = await this.checkMessagDocs(oauthConfig, version)
|
||||
const resourceDocs = await this.checkResourceDocs(oauthConfig, version)
|
||||
return response.json({
|
||||
status: apiVersions && messageDocs && resourceDocs,
|
||||
apiVersions,
|
||||
messageDocs,
|
||||
resourceDocs
|
||||
})
|
||||
}
|
||||
|
||||
isCodeError(response: any, path: string): boolean {
|
||||
console.log(`Validating ${path} response...`)
|
||||
if (!response || Object.keys(response).length == 0) return true
|
||||
if (Object.keys(response).includes('code')) {
|
||||
const code = response['code']
|
||||
if (code >= 400) {
|
||||
console.log(response) // Log error responce
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
async checkResourceDocs(oauthConfig: OAuthConfig, version: string): Promise<boolean> {
|
||||
try {
|
||||
const path = `/obp/${version}/resource-docs/${version}/obp`
|
||||
const resourceDocs = await this.obpClientService.get(
|
||||
path,
|
||||
oauthConfig
|
||||
)
|
||||
return !this.isCodeError(resourceDocs, path)
|
||||
} catch (error) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
async checkMessagDocs(oauthConfig: OAuthConfig, version: string): Promise<boolean> {
|
||||
try {
|
||||
const messageDocsCodeResult = await Promise.all(
|
||||
this.connectors.map(async (connector) => {
|
||||
const path = `/obp/${version}/message-docs/${connector}`
|
||||
return !this.isCodeError(
|
||||
await this.obpClientService.get(
|
||||
path,
|
||||
oauthConfig
|
||||
),
|
||||
path
|
||||
)
|
||||
})
|
||||
)
|
||||
return messageDocsCodeResult.every((isCodeError: boolean) => isCodeError)
|
||||
} catch (error) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
async checkApiVersions(oauthConfig: OAuthConfig, version: string): Promise<boolean> {
|
||||
try {
|
||||
const path = `/obp/${version}/api/versions`
|
||||
const versions = await this.obpClientService.get(path, oauthConfig)
|
||||
return !this.isCodeError(versions, path)
|
||||
} catch (error) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -16,6 +16,7 @@ export default class OauthAccessTokenMiddleware implements ExpressMiddlewareInte
|
||||
const consumer = oauthService.getConsumer()
|
||||
const oauthVerifier = request.query.oauth_verifier
|
||||
const session = request.session
|
||||
console.log('OauthAccessTokenMiddleware.ts use says: Before consumer.getOAuthAccessToken')
|
||||
consumer.getOAuthAccessToken(
|
||||
oauthService.requestTokenKey,
|
||||
oauthService.requestTokenSecret,
|
||||
@ -34,6 +35,7 @@ export default class OauthAccessTokenMiddleware implements ExpressMiddlewareInte
|
||||
secret: oauthTokenSecret
|
||||
}
|
||||
session['clientConfig'] = clientConfig
|
||||
console.log('OauthAccessTokenMiddleware.ts use says: Seems OK, redirecting..')
|
||||
response.redirect(`${process.env.VITE_OBP_EXPLORER_HOST}`)
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,6 +19,7 @@ export default class OauthRequestTokenMiddleware implements ExpressMiddlewareInt
|
||||
} else {
|
||||
oauthService.requestTokenKey = oauthTokenKey
|
||||
oauthService.requestTokenSecret = oauthTokenSecret
|
||||
console.log('OauthRequestTokenMiddleware.ts consumer.getOAuthRequestToken says: Redirecting to /oauth/authorize?oauth_token=XXX')
|
||||
response.redirect(apiHost + '/oauth/authorize?oauth_token=' + oauthTokenKey)
|
||||
}
|
||||
})
|
||||
|
||||
@ -50,10 +50,6 @@
|
||||
font-family: Roboto;
|
||||
}
|
||||
|
||||
.el-dropdown-menu__item:hover {
|
||||
color: #52b165 !important;
|
||||
}
|
||||
|
||||
.el-alert--info.is-light {
|
||||
background-color: #253047;
|
||||
}
|
||||
|
||||
@ -34,3 +34,9 @@ main {
|
||||
.favoriteButton:hover {
|
||||
color: #fcc406;
|
||||
}
|
||||
|
||||
.search-nav {
|
||||
background-color: #f8f9fb;
|
||||
padding: 8px;
|
||||
border-right: solid 1px var(--el-menu-border-color);
|
||||
}
|
||||
|
||||
59
src/components/Collections.vue
Normal file
59
src/components/Collections.vue
Normal file
@ -0,0 +1,59 @@
|
||||
<script setup lang="ts">
|
||||
import { SEARCH_LINKS_COLOR as searchLinksColorSetting } from '../obp/style-setting'
|
||||
import { inject, ref } from 'vue'
|
||||
|
||||
const searchLinksColor = ref(searchLinksColorSetting);
|
||||
|
||||
const collectionData = [
|
||||
{
|
||||
"url": "/?api-collection-id=341bf039-97e4-4803-aaae-2d1dc6a5b162",
|
||||
"text": "Risk mgmt. & mitigation"
|
||||
},
|
||||
{
|
||||
"url": "/?api-collection-id=fc3220e5-c891-4943-bbdb-6579ef3889c0",
|
||||
"text": "Process mgmt. & enh."
|
||||
},
|
||||
{
|
||||
"url": "/?api-collection-id=18fa516e-8e52-473c-98c4-430b1043ef4f",
|
||||
"text": "Credit data & scoring"
|
||||
},
|
||||
{
|
||||
"url": "/?api-collection-id=7d4b5f73-9bde-4556-a0ee-ce0f746f833f",
|
||||
"text": "Innovative prods & svcs"
|
||||
},
|
||||
{
|
||||
"url": "/?api-collection-id=cbd97e62-fb8a-42a4-ad5a-139729d11312",
|
||||
"text": "Women-led/owned MSMEs prods & svcs"
|
||||
}
|
||||
]
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-menu
|
||||
class="collections-menu"
|
||||
mode="horizontal"
|
||||
background-color="#151d30"
|
||||
text-color="#fff"
|
||||
active-text-color="#52b165">
|
||||
<el-menu-item v-for="(item, index) in collectionData" :index="index">
|
||||
<RouterLink :to="item.url">{{ item.text }}</RouterLink>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
a {
|
||||
font-size: 14px;
|
||||
font-family: 'Roboto';
|
||||
color: #7787a6;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* This is not working at the moment, but ideally we would like the color of the menu item to change when hovering.
|
||||
a:hover {
|
||||
color: v-bind(searchLinksColor);
|
||||
}
|
||||
*/
|
||||
|
||||
</style>
|
||||
@ -1,58 +1,69 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, inject, provide, onActivated, onMounted } from 'vue'
|
||||
import { onBeforeRouteUpdate, useRoute } from 'vue-router'
|
||||
import { getOperationDetails } from '../obp/resource-docs'
|
||||
import { obpMyCollectionsEndpointKey, obpResourceDocsKey } from '@/obp/keys'
|
||||
import { ArrowLeftBold, ArrowRightBold } from '@element-plus/icons-vue'
|
||||
import type { ElNotification } from 'element-plus'
|
||||
import { ElNotification } from 'element-plus'
|
||||
import { inject, onMounted, provide, ref } from 'vue'
|
||||
import { onBeforeRouteUpdate, useRoute } from 'vue-router'
|
||||
import {
|
||||
createMyAPICollection,
|
||||
createMyAPICollectionEndpoint,
|
||||
deleteMyAPICollectionEndpoint,
|
||||
getCurrentUser
|
||||
OBP_API_VERSION,
|
||||
createMyAPICollection,
|
||||
createMyAPICollectionEndpoint,
|
||||
deleteMyAPICollectionEndpoint,
|
||||
getCurrentUser
|
||||
} from '../obp'
|
||||
import { setTabActive, initializeAPICollections } from './SearchNav.vue'
|
||||
import { getGroupedResourceDocs, getOperationDetails } from '../obp/resource-docs'
|
||||
import { SUMMARY_PAGER_LINKS_COLOR as summaryPagerLinksColorSetting } from '../obp/style-setting'
|
||||
import { initializeAPICollections, setTabActive } from './SearchNav.vue'
|
||||
|
||||
const route = useRoute()
|
||||
const obpVersion = 'OBP' + OBP_API_VERSION
|
||||
const description = ref('')
|
||||
const summary = ref('')
|
||||
const docs = inject('OBP-ResourceDocs')
|
||||
const resourceDocs = inject(obpResourceDocsKey)
|
||||
const docs = getGroupedResourceDocs(obpVersion, resourceDocs)
|
||||
const displayPrev = ref(true)
|
||||
const displayNext = ref(true)
|
||||
const prev = ref({ id: 'prev' })
|
||||
const next = ref({ id: 'next' })
|
||||
const favoriteButtonStyle = ref('favorite favoriteButton')
|
||||
const summaryPagerLinksColor = ref(summaryPagerLinksColorSetting)
|
||||
let routeId = ''
|
||||
let version = obpVersion
|
||||
let isFavorite = false
|
||||
let apiCollectionsEndpoint = inject('OBP-MyCollectionsEndpoint')!
|
||||
let apiCollectionsEndpoint = inject(obpMyCollectionsEndpointKey)!
|
||||
|
||||
const setOperationDetails = (id: string): void => {
|
||||
const operation = getOperationDetails(docs, id)
|
||||
description.value = operation.description
|
||||
summary.value = operation.summary
|
||||
const setOperationDetails = (id: string, version: string): void => {
|
||||
const operation = getOperationDetails(version, id, resourceDocs)
|
||||
description.value = operation?.description
|
||||
summary.value = operation?.summary
|
||||
}
|
||||
|
||||
const setPager = (id: string): void => {
|
||||
const target = document.getElementById(id).parentElement
|
||||
const prevElement = target.previousSibling
|
||||
const nextElement = target.nextSibling
|
||||
const active = document.querySelector('.active-api-router-tab')
|
||||
if (active) active.classList.remove('active-api-router-tab')
|
||||
target.classList.add('active-api-router-tab')
|
||||
if (prevElement.className && prevElement.className.startsWith('api-router-tab')) {
|
||||
const prevItem = prevElement.children.item(0)
|
||||
prev.value['title'] = prevItem.text
|
||||
prev.value['id'] = prevItem.id
|
||||
displayPrev.value = true
|
||||
} else {
|
||||
displayPrev.value = false
|
||||
}
|
||||
if (nextElement.className && nextElement.className.startsWith('api-router-tab')) {
|
||||
const nextItem = nextElement.children.item(0)
|
||||
next.value['title'] = nextItem.text
|
||||
next.value['id'] = nextItem.id
|
||||
displayNext.value = true
|
||||
} else {
|
||||
displayNext.value = false
|
||||
const target = document.getElementById(id)?.parentElement
|
||||
if (target) {
|
||||
const prevElement = target.previousSibling
|
||||
const nextElement = target.nextSibling
|
||||
const active = document.querySelector('.active-api-router-tab')
|
||||
if (active) active.classList.remove('active-api-router-tab')
|
||||
target.classList.add('active-api-router-tab')
|
||||
if (prevElement.className && prevElement.className.startsWith('api-router-tab')) {
|
||||
const prevItem = prevElement.children.item(0)
|
||||
prev.value['title'] = prevItem.text
|
||||
prev.value['id'] = prevItem.id
|
||||
prev.value['version'] = version
|
||||
displayPrev.value = true
|
||||
} else {
|
||||
displayPrev.value = false
|
||||
}
|
||||
if (nextElement.className && nextElement.className.startsWith('api-router-tab')) {
|
||||
const nextItem = nextElement.children.item(0)
|
||||
next.value['title'] = nextItem.text
|
||||
next.value['id'] = nextItem.id
|
||||
next.value['version'] = version
|
||||
displayNext.value = true
|
||||
} else {
|
||||
displayNext.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -77,20 +88,21 @@ const createDeleteFavorite = async (): void => {
|
||||
createMyAPICollection()
|
||||
apiCollectionsEndpoint = []
|
||||
}
|
||||
if (isFavorite) {
|
||||
await deleteMyAPICollectionEndpoint(routeId)
|
||||
if (isFavorite) { // Add the API endpoint to favorite
|
||||
const response = await deleteMyAPICollectionEndpoint(routeId)
|
||||
favoriteButtonStyle.value = 'favorite favoriteButton'
|
||||
showNotification('Removed from favourites.', 'success')
|
||||
if (response) { // Success response returns <empty string>
|
||||
showNotification(response, 'error')
|
||||
}
|
||||
isFavorite = false
|
||||
apiCollectionsEndpoint = apiCollectionsEndpoint.filter((api) => api != routeId)
|
||||
} else {
|
||||
await createMyAPICollectionEndpoint(routeId)
|
||||
} else { // Remove the API endpoint from favorite
|
||||
const response = await createMyAPICollectionEndpoint(routeId)
|
||||
favoriteButtonStyle.value = 'favorite activeFavoriteButton'
|
||||
showNotification('Added to favourites.', 'success')
|
||||
isFavorite = true
|
||||
apiCollectionsEndpoint.push(routeId)
|
||||
}
|
||||
provide('OBP-MyCollectionsEndpoint', apiCollectionsEndpoint)
|
||||
provide(obpMyCollectionsEndpointKey, apiCollectionsEndpoint)
|
||||
await initializeAPICollections()
|
||||
setTabActive(routeId)
|
||||
}
|
||||
@ -98,7 +110,6 @@ const createDeleteFavorite = async (): void => {
|
||||
const showNotification = (message: string, type: string): void => {
|
||||
ElNotification({
|
||||
duration: 5500,
|
||||
position: 'bottom-right',
|
||||
message,
|
||||
type
|
||||
})
|
||||
@ -106,13 +117,15 @@ const showNotification = (message: string, type: string): void => {
|
||||
|
||||
onMounted(async () => {
|
||||
routeId = route.params.id
|
||||
setOperationDetails(routeId)
|
||||
version = route.query.version ? route.query.version : obpVersion
|
||||
setOperationDetails(routeId, version)
|
||||
setPager(routeId)
|
||||
await tagFavoriteButton(routeId)
|
||||
})
|
||||
onBeforeRouteUpdate(async (to) => {
|
||||
routeId = to.params.id
|
||||
setOperationDetails(routeId)
|
||||
version = route.query.version ? route.query.version : obpVersion
|
||||
setOperationDetails(routeId, version)
|
||||
setPager(routeId)
|
||||
await tagFavoriteButton(routeId)
|
||||
})
|
||||
@ -137,22 +150,20 @@ onBeforeRouteUpdate(async (to) => {
|
||||
<el-divider class="divider" />
|
||||
<el-row>
|
||||
<el-col :span="12" class="pager-left">
|
||||
<el-icon v-show="displayPrev"><ArrowLeftBold /></el-icon>
|
||||
<RouterLink
|
||||
v-show="displayPrev"
|
||||
class="pager-router-link"
|
||||
:to="{ name: 'api', params: { id: prev.id } }"
|
||||
>{{ prev.title }}</RouterLink
|
||||
>
|
||||
<el-icon v-show="displayPrev">
|
||||
<ArrowLeftBold />
|
||||
</el-icon>
|
||||
<RouterLink v-show="displayPrev" class="pager-router-link"
|
||||
:to="{ name: 'api', params: { id: prev.id }, query: { version: prev.version } }">{{ prev.title }}
|
||||
</RouterLink>
|
||||
</el-col>
|
||||
<el-col :span="12" class="pager-right">
|
||||
<RouterLink
|
||||
v-show="displayNext"
|
||||
class="pager-router-link"
|
||||
:to="{ name: 'api', params: { id: next.id } }"
|
||||
>{{ next.title }}</RouterLink
|
||||
>
|
||||
<el-icon v-show="displayNext"><ArrowRightBold /></el-icon>
|
||||
<RouterLink v-show="displayNext" class="pager-router-link"
|
||||
:to="{ name: 'api', params: { id: next.id }, query: { version: next.version } }">{{ next.title }}
|
||||
</RouterLink>
|
||||
<el-icon v-show="displayNext">
|
||||
<ArrowRightBold />
|
||||
</el-icon>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-footer>
|
||||
@ -166,15 +177,19 @@ main {
|
||||
color: #39455f;
|
||||
font-family: 'Roboto';
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
div {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.content :deep(strong) {
|
||||
font-family: 'Roboto';
|
||||
}
|
||||
|
||||
.content :deep(p a) {
|
||||
line-height: 28px;
|
||||
padding: 5px;
|
||||
@ -186,38 +201,45 @@ div {
|
||||
border-radius: 5px;
|
||||
background-color: #eef0f4;
|
||||
}
|
||||
|
||||
.pager {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.pager-left {
|
||||
display: flex;
|
||||
justify-content: left;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.pager-right {
|
||||
display: flex;
|
||||
justify-content: right;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.footer {
|
||||
max-height: 30px;
|
||||
}
|
||||
|
||||
.divider {
|
||||
margin-top: -15px;
|
||||
}
|
||||
|
||||
.pager-router-link {
|
||||
font-family: 'Roboto';
|
||||
text-decoration: none;
|
||||
color: #39455f;
|
||||
}
|
||||
|
||||
.pager-router-link:hover,
|
||||
.pager-left:hover,
|
||||
.pager-right:hover {
|
||||
color: #52b165;
|
||||
color: v-bind(summaryPagerLinksColor);
|
||||
}
|
||||
|
||||
.favorite {
|
||||
cursor: pointer;
|
||||
line-height: 1;
|
||||
}
|
||||
</style>
|
||||
}</style>
|
||||
|
||||
@ -1,12 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref, onBeforeMount, inject } from 'vue'
|
||||
import { Search } from '@element-plus/icons-vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { obpGlossaryKey } from '@/obp/keys';
|
||||
import { Search } from '@element-plus/icons-vue';
|
||||
import { inject, onBeforeMount, onMounted, reactive, ref } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { SEARCH_LINKS_COLOR as searchLinksColorSetting } from '../obp/style-setting';
|
||||
|
||||
const route = useRoute()
|
||||
const activeKeys = ref([])
|
||||
const glossaryKeys = ref([])
|
||||
const alphabet = ref([])
|
||||
const searchLinksColor = ref(searchLinksColorSetting)
|
||||
const form = reactive({
|
||||
search: ''
|
||||
})
|
||||
@ -15,7 +18,7 @@ const alphabetCharCodes = Array.from(Array(26)).map((e, i) => i + 65)
|
||||
alphabet.value = alphabetCharCodes.map((x) => String.fromCharCode(x))
|
||||
|
||||
onBeforeMount(() => {
|
||||
const glossary = inject('OBP-Glossary')!
|
||||
const glossary = inject(obpGlossaryKey)!
|
||||
for (const item of glossary.glossary_items) {
|
||||
if (!activeKeys.value.includes(item.title)) {
|
||||
activeKeys.value.push(item.title)
|
||||
@ -24,6 +27,16 @@ onBeforeMount(() => {
|
||||
glossaryKeys.value = activeKeys.value
|
||||
})
|
||||
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
let hash = route.hash;
|
||||
let elements = document.querySelectorAll(`a[href="${hash}"][class="glossary-router-link"]`)
|
||||
if (elements.length == 1) {
|
||||
elements[0].click()
|
||||
}
|
||||
})
|
||||
|
||||
const filterKeys = (keys, key) => {
|
||||
return keys.filter((title) => {
|
||||
return title.toLowerCase().includes(key.toLowerCase())
|
||||
@ -40,21 +53,11 @@ const searchEvent = (event) => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-input
|
||||
v-model="form.search"
|
||||
class="w-50 m-1"
|
||||
placeholder="Search"
|
||||
:prefix-icon="Search"
|
||||
@input="searchEvent"
|
||||
/>
|
||||
<el-input v-model="form.search" class="w-50 m-1" placeholder="Search" :prefix-icon="Search" @input="searchEvent" />
|
||||
<div class="tabs">
|
||||
<div class="alphabet">
|
||||
<div v-for="value of alphabet" :key="value">
|
||||
<a
|
||||
:id="value"
|
||||
class="alphabet-router-link"
|
||||
v-bind:href="`#${value.toLowerCase()}-quick-nav`"
|
||||
>
|
||||
<a :id="value" class="alphabet-router-link" v-bind:href="`#${value.toLowerCase()}-quick-nav`">
|
||||
<div class="alphabet-link">
|
||||
{{ value }}
|
||||
</div>
|
||||
@ -64,11 +67,7 @@ const searchEvent = (event) => {
|
||||
<div class="tab-items">
|
||||
<div class="el-tabs--right">
|
||||
<div v-for="value of glossaryKeys" :key="value" class="glossary-router-tab">
|
||||
<a
|
||||
class="glossary-router-link"
|
||||
:id="`${value.charAt(0).toLowerCase()}-quick-nav`"
|
||||
v-bind:href="`#${value}`"
|
||||
>
|
||||
<a class="glossary-router-link" :id="`${value.charAt(0).toLowerCase()}-quick-nav`" v-bind:href="`#${value}`">
|
||||
{{ value }}
|
||||
</a>
|
||||
</div>
|
||||
@ -77,32 +76,30 @@ const searchEvent = (event) => {
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
<style scoped>
|
||||
.tabs {
|
||||
display: flex;
|
||||
max-height: 90vh;
|
||||
}
|
||||
|
||||
.alphabet {
|
||||
padding: 10px 5px 5px 5px;
|
||||
min-width: 25px;
|
||||
}
|
||||
|
||||
.alphabet-link {
|
||||
padding: 5px 0px 5px 0px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.alphabet-router-link {
|
||||
font-size: 13px;
|
||||
font-family: 'Roboto';
|
||||
color: #39455f;
|
||||
text-decoration: none;
|
||||
}
|
||||
.search-nav {
|
||||
background-color: #f8f9fb;
|
||||
padding: 8px;
|
||||
border-right: solid 1px var(--el-menu-border-color);
|
||||
}
|
||||
|
||||
.glossary-router-link {
|
||||
margin-left: 15px;
|
||||
@ -120,15 +117,16 @@ const searchEvent = (event) => {
|
||||
|
||||
.glossary-router-tab:hover,
|
||||
.active-glossary-router-tab {
|
||||
border-left: 2px solid #52b165;
|
||||
border-left: 2px solid v-bind(searchLinksColor);
|
||||
}
|
||||
|
||||
.glossary-router-tab:hover .glossary-router-link,
|
||||
.active-glossary-router-link,
|
||||
.alphabet-router-link:hover,
|
||||
.alphabet-link:hover .alphabet-router-link {
|
||||
color: #52b165;
|
||||
color: v-bind(searchLinksColor);
|
||||
}
|
||||
|
||||
.tab-items {
|
||||
overflow: auto;
|
||||
max-height: 100vh;
|
||||
|
||||
@ -1,28 +1,58 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, watchEffect, onMounted } from 'vue'
|
||||
import { ref, inject, watchEffect, onMounted } from 'vue'
|
||||
import { ArrowDown } from '@element-plus/icons-vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { getCurrentUser } from '../obp'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { OBP_API_VERSION, getCurrentUser } from '../obp'
|
||||
import { getOBPAPIVersions } from '../obp/api-version'
|
||||
import {
|
||||
LOGO_URL as logoSource,
|
||||
HEADER_LINKS_COLOR,
|
||||
HEADER_LINKS_HOVER_COLOR as headerLinksHoverColorSetting,
|
||||
HEADER_LINKS_BACKGROUND_COLOR as headerLinksBackgroundColorSetting
|
||||
} from '../obp/style-setting'
|
||||
import { obpApiActiveVersionsKey, obpGroupedMessageDocsKey, obpMyCollectionsEndpointKey } from '@/obp/keys'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const obpApiHost = ref(import.meta.env.VITE_OBP_API_HOST)
|
||||
const obpApiManagerHost = ref(import.meta.env.VITE_OBP_API_MANAGER_HOST)
|
||||
const loginUsername = ref('')
|
||||
const logOffUrl = ref('')
|
||||
const logoffurl = ref('')
|
||||
const obpApiVersions = ref(inject(obpApiActiveVersionsKey)!)
|
||||
const obpMessageDocs = ref(Object.keys(inject(obpGroupedMessageDocsKey)!))
|
||||
const isShowLoginButton = ref(true)
|
||||
const isShowLogOffButton = ref(false)
|
||||
const logo = ref(logoSource)
|
||||
const headerLinksHoverColor = ref(headerLinksHoverColorSetting)
|
||||
const headerLinksBackgroundColor = ref(headerLinksBackgroundColorSetting)
|
||||
|
||||
const clearActiveTab = () => {
|
||||
const activeLinks = document.querySelectorAll('.router-link')
|
||||
for (const active of activeLinks) {
|
||||
if (active.id) active.style.backgroundColor = 'transparent'
|
||||
if (active.id) {
|
||||
active.style.backgroundColor = 'transparent'
|
||||
active.style.color = '#39455f'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const setActive = (target) => {
|
||||
if (target) {
|
||||
clearActiveTab()
|
||||
target.style.backgroundColor = '#eef0f4'
|
||||
target.style.backgroundColor = headerLinksBackgroundColor.value
|
||||
target.style.color = HEADER_LINKS_COLOR
|
||||
}
|
||||
}
|
||||
|
||||
const handleMore = (command: string) => {
|
||||
let element = document.getElementById("selected-api-version")
|
||||
if (element !== null) {
|
||||
element.textContent = command;
|
||||
}
|
||||
if (command.includes('_')) {
|
||||
router.push({ name: 'message-docs', params: { id: command } })
|
||||
} else {
|
||||
router.replace({ path: '/operationid', query: { version: command } })
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,21 +74,25 @@ watchEffect(() => {
|
||||
if (path && route.params && !route.params.id) {
|
||||
setActive(document.getElementById('header-nav-' + path))
|
||||
} else {
|
||||
setActive(document.getElementById('header-nav-tags'))
|
||||
if (path == 'message-docs') {
|
||||
clearActiveTab()
|
||||
} else {
|
||||
setActive(document.getElementById('header-nav-tags'))
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<img alt="OBP logo" class="logo" src="@/assets/logo2x-1.png" />
|
||||
<img alt="OBP logo" class="logo" v-show="logo" :src="logo" />
|
||||
<img alt="OBP logo" class="logo" v-show="!logo" src="@/assets/logo2x-1.png" />
|
||||
<nav id="nav">
|
||||
<RouterView name="header">
|
||||
<a v-bind:href="obpApiHost" class="router-link" id="header-nav-home">
|
||||
{{ $t('header.portal_home') }}
|
||||
</a>
|
||||
<RouterLink class="router-link" id="header-nav-tags" to="/operationid">{{
|
||||
$t('header.api_explorer')
|
||||
}}</RouterLink>
|
||||
<RouterLink class="router-link" id="header-nav-tags" :to="'/operationid?version=OBP' + OBP_API_VERSION">{{
|
||||
$t('header.api_explorer') }}</RouterLink>
|
||||
<RouterLink class="router-link" id="header-nav-glossary" to="/glossary">{{
|
||||
$t('header.glossary')
|
||||
}}</RouterLink>
|
||||
@ -66,7 +100,7 @@ watchEffect(() => {
|
||||
{{ $t('header.api_manager') }}
|
||||
</a>
|
||||
<span class="el-dropdown-link">
|
||||
<el-dropdown class="menu-right router-link" id="header-nav-spaces">
|
||||
<el-dropdown class="menu-right router-link" id="header-nav-more" @command="handleMore">
|
||||
<span class="el-dropdown-link">
|
||||
{{ $t('header.more') }}
|
||||
<el-icon class="el-icon--right">
|
||||
@ -75,7 +109,11 @@ watchEffect(() => {
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item key="messageDocs">Message Docs</el-dropdown-item>
|
||||
<el-dropdown-item v-for="value in obpApiVersions" :command="value" key="value">{{
|
||||
value
|
||||
}}</el-dropdown-item>
|
||||
<el-dropdown-item v-for="value in obpMessageDocs" :command="value" key="value">
|
||||
Message Docs for: {{ value }}</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
@ -92,11 +130,7 @@ watchEffect(() => {
|
||||
{{ $t('header.login') }}
|
||||
</a>
|
||||
<span v-show="isShowLogOffButton" class="login-user">{{ loginUsername }}</span>
|
||||
<a
|
||||
v-bind:href="'/api/user/logoff'"
|
||||
v-show="isShowLogOffButton"
|
||||
class="logoff-button router-link"
|
||||
>
|
||||
<a v-bind:href="'/api/user/logoff'" v-show="isShowLogOffButton" class="logoff-button router-link">
|
||||
{{ $t('header.logoff') }}
|
||||
</a>
|
||||
</RouterView>
|
||||
@ -152,7 +186,8 @@ nav {
|
||||
}
|
||||
|
||||
.router-link:hover {
|
||||
background-color: #eef0f4 !important;
|
||||
background-color: v-bind(headerLinksBackgroundColor) !important;
|
||||
color: v-bind(headerLinksHoverColor) !important;
|
||||
}
|
||||
|
||||
.logo {
|
||||
@ -173,4 +208,9 @@ nav {
|
||||
.logoff-button:hover {
|
||||
color: #39455f;
|
||||
}
|
||||
|
||||
/*override element plus*/
|
||||
.el-dropdown-menu__item:hover {
|
||||
color: v-bind(headerLinksHoverColor) !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,25 +1,38 @@
|
||||
<script setup lang="ts">
|
||||
import { ArrowDown } from '@element-plus/icons-vue'
|
||||
import { inject } from 'vue'
|
||||
import { SEARCH_LINKS_COLOR as searchLinksColorSetting } from '../obp/style-setting'
|
||||
import { inject, ref } from 'vue'
|
||||
import { updateServerStatus } from '@/obp/common-functions';
|
||||
import { obpApiHostKey } from '@/obp/keys';
|
||||
|
||||
const APP_VERSION = ref(__APP_VERSION__)
|
||||
const i18n = inject('i18n')
|
||||
const host = inject('OBP-API-Host')
|
||||
const OBP_API_HOST = inject(obpApiHostKey)
|
||||
const searchLinksColor = ref(searchLinksColorSetting)
|
||||
const handleLocale = (command: string) => {
|
||||
i18n.global.locale.value = command
|
||||
}
|
||||
const updateStatus = (event: any) => {
|
||||
updateServerStatus()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-row>
|
||||
<el-col :span="12" class="menu-left"></el-col>
|
||||
<el-col :span="12" class="menu-right">
|
||||
<el-col :span="10" class="menu-left">
|
||||
|
||||
<span id="selected-api-version" class="host">OBPv5.1.0</span>
|
||||
</el-col>
|
||||
<el-col :span="14" class="menu-right">
|
||||
<span class="host">App Version: {{ APP_VERSION }}</span>
|
||||
|
||||
<span class="host"
|
||||
>API Host:
|
||||
<a :href="host">
|
||||
{{ host }}
|
||||
><span id="backend-status" @click="updateStatus" >API Host: </span>
|
||||
<a :href="OBP_API_HOST">
|
||||
{{ OBP_API_HOST }}
|
||||
</a>
|
||||
</span>
|
||||
|
||||
|
||||
<el-dropdown class="menu-right" @command="handleLocale">
|
||||
<span class="el-dropdown-link">
|
||||
{{ $i18n.locale }}
|
||||
@ -50,14 +63,21 @@ a {
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
color: #52b165;
|
||||
color: v-bind(searchLinksColor);
|
||||
}
|
||||
.host {
|
||||
font-size: 14px;
|
||||
font-family: 'Roboto';
|
||||
}
|
||||
.menu-left,
|
||||
.menu-right,
|
||||
.el-dropdown-menu {
|
||||
color: #7787a6;
|
||||
}
|
||||
.server-is-online {
|
||||
color: v-bind(searchLinksColor);
|
||||
}
|
||||
.server-is-offline {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
|
||||
141
src/components/MessageDocsSearchNav.vue
Normal file
141
src/components/MessageDocsSearchNav.vue
Normal file
@ -0,0 +1,141 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref, onBeforeMount, inject, watch } from 'vue'
|
||||
import { Search } from '@element-plus/icons-vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { SEARCH_LINKS_COLOR as searchLinksColorSetting } from '../obp/style-setting'
|
||||
import { connectors } from '../obp/message-docs'
|
||||
import { obpGroupedMessageDocsKey } from '@/obp/keys'
|
||||
|
||||
let connector = connectors[0]
|
||||
const route = useRoute()
|
||||
const groupedMessageDocs = ref(inject(obpGroupedMessageDocsKey)!)
|
||||
const docs = ref({})
|
||||
const groups = ref({})
|
||||
const sortedKeys = ref([])
|
||||
const activeKeys = ref([])
|
||||
const messageDocKeys = ref([])
|
||||
const searchLinksColor = ref(searchLinksColorSetting)
|
||||
const form = reactive({
|
||||
search: ''
|
||||
})
|
||||
|
||||
onBeforeMount(() => {
|
||||
setDocs()
|
||||
})
|
||||
|
||||
watch(
|
||||
() => route.params.id,
|
||||
async (id) => {
|
||||
setDocs()
|
||||
}
|
||||
)
|
||||
|
||||
const isKeyFound = (keys, item) => keys.every((k) => item.toLowerCase().includes(k))
|
||||
|
||||
const filterKeys = (keys, key) => {
|
||||
const splitKey = key.split(' ').map((k) => k.toLowerCase())
|
||||
return keys.filter((title) => {
|
||||
const isGroupFound = isKeyFound(splitKey, title)
|
||||
const items = docs.value[title].filter((item) => isGroupFound || isKeyFound(splitKey, item))
|
||||
groups.value[title] = items
|
||||
return isGroupFound || items.length > 0
|
||||
})
|
||||
}
|
||||
|
||||
const searchEvent = (value) => {
|
||||
if (value) {
|
||||
messageDocKeys.value = filterKeys(activeKeys.value, value)
|
||||
} else {
|
||||
groups.value = JSON.parse(JSON.stringify(docs.value))
|
||||
messageDocKeys.value = Object.keys(groups.value)
|
||||
}
|
||||
}
|
||||
|
||||
const setDocs = () => {
|
||||
const paramConnector = route.params.id
|
||||
if (connectors.includes(paramConnector)) {
|
||||
connector = paramConnector
|
||||
}
|
||||
const messageDocs = groupedMessageDocs.value[connector]
|
||||
|
||||
docs.value = Object.keys(messageDocs).reduce((doc, key) => {
|
||||
doc[key] = messageDocs[key].map((group) => group.process)
|
||||
return doc
|
||||
}, {})
|
||||
groups.value = JSON.parse(JSON.stringify(docs.value))
|
||||
messageDocKeys.value = Object.keys(groups.value)
|
||||
activeKeys.value = Object.keys(groups.value)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-input
|
||||
v-model="form.search"
|
||||
placeholder="Search"
|
||||
:prefix-icon="Search"
|
||||
@input="searchEvent"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-collapse v-model="activeKeys">
|
||||
<el-collapse-item v-for="key in messageDocKeys" :title="key" :key="key" :name="key">
|
||||
<div class="el-tabs--right">
|
||||
<div v-for="(value, key) of groups[key]" :key="value" class="message-docs-router-tab">
|
||||
<a class="message-docs-router-link" :id="`${value}-quick-nav`" v-bind:href="`#${value}`">
|
||||
{{ value }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.api-router-link {
|
||||
width: 100%;
|
||||
margin-left: 15px;
|
||||
font-family: 'Roboto';
|
||||
text-decoration: none;
|
||||
color: #39455f;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.api-router-tab {
|
||||
border-left: 2px solid var(--el-menu-border-color);
|
||||
}
|
||||
|
||||
.api-router-tab:hover,
|
||||
.active-api-router-tab {
|
||||
border-left: 2px solid v-bind(searchLinksColor);
|
||||
}
|
||||
|
||||
.api-router-tab:hover .api-router-link,
|
||||
.active-api-router-link {
|
||||
color: v-bind(searchLinksColor);
|
||||
}
|
||||
|
||||
.message-docs-router-link {
|
||||
margin-left: 15px;
|
||||
font-size: 13px;
|
||||
font-family: 'Roboto';
|
||||
text-decoration: none;
|
||||
color: #39455f;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.message-docs-router-tab {
|
||||
border-left: 2px solid var(--el-menu-border-color);
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
.message-docs-router-tab:hover,
|
||||
.active-message-docs-router-tab {
|
||||
border-left: 2px solid v-bind(searchLinksColor);
|
||||
}
|
||||
|
||||
.message-docs-router-tab:hover .message-docs-router-link {
|
||||
color: v-bind(searchLinksColor);
|
||||
}
|
||||
</style>
|
||||
@ -3,9 +3,12 @@ import { ref, reactive, inject, onBeforeMount } from 'vue'
|
||||
import { onBeforeRouteUpdate, useRoute } from 'vue-router'
|
||||
import { getOperationDetails } from '../obp/resource-docs'
|
||||
import type { ElNotification, FormInstance } from 'element-plus'
|
||||
import { get, create, update, discard, createEntitlement, getCurrentUser } from '../obp'
|
||||
import { OBP_API_VERSION, get, create, update, discard, createEntitlement, getCurrentUser } from '../obp'
|
||||
import { getGroupedResourceDocs } from '../obp/resource-docs'
|
||||
import { obpResourceDocsKey } from '@/obp/keys'
|
||||
|
||||
const elMessageDuration = 5500
|
||||
const configVersion = 'OBP' + OBP_API_VERSION
|
||||
const url = ref('')
|
||||
const roleName = ref('')
|
||||
const method = ref('')
|
||||
@ -23,7 +26,14 @@ const showPossibleErrors = ref(true)
|
||||
const showConnectorMethods = ref(true)
|
||||
const isUserLogon = ref(true)
|
||||
const type = ref('')
|
||||
const docs = inject('OBP-ResourceDocs')
|
||||
const resourceDocs = inject(obpResourceDocsKey)
|
||||
const docs = getGroupedResourceDocs(configVersion, resourceDocs)
|
||||
const footNote = ref({
|
||||
operationId: '',
|
||||
version: '',
|
||||
functionName: '',
|
||||
messageTags: ''
|
||||
})
|
||||
|
||||
const requestFormRef = reactive<FormInstance>({})
|
||||
const requestForm = reactive({ url: '' })
|
||||
@ -31,10 +41,10 @@ const requestForm = reactive({ url: '' })
|
||||
const roleFormRef = reactive<FormInstance>({})
|
||||
const roleForm = reactive({})
|
||||
|
||||
const setOperationDetails = (id: string): void => {
|
||||
const operation = getOperationDetails(docs, id)
|
||||
url.value = operation.specified_url
|
||||
method.value = operation.request_verb
|
||||
const setOperationDetails = (id: string, version: string): void => {
|
||||
const operation = getOperationDetails(version, id, resourceDocs)
|
||||
url.value = operation?.specified_url
|
||||
method.value = operation?.request_verb
|
||||
exampleRequestBody.value = JSON.stringify(operation.example_request_body)
|
||||
requiredRoles.value = operation.roles || []
|
||||
possibleErrors.value = operation.error_response_bodies
|
||||
@ -43,6 +53,10 @@ const setOperationDetails = (id: string): void => {
|
||||
showValidations.value = validations.value.length > 0
|
||||
showPossibleErrors.value = possibleErrors.value.length > 0
|
||||
showConnectorMethods.value = connectorMethods.value.length > 0
|
||||
footNote.value.version = operation.operation_id
|
||||
footNote.value.version = operation.implemented_by.version
|
||||
footNote.value.functionName = operation.implemented_by.function
|
||||
footNote.value.messageTags = operation.tags.join(',')
|
||||
|
||||
highlightCode(operation.success_response_body)
|
||||
setType(method.value)
|
||||
@ -103,8 +117,7 @@ const submitRequest = async () => {
|
||||
ElNotification({
|
||||
duration: elMessageDuration,
|
||||
message: 'URL path is required.',
|
||||
type: 'error',
|
||||
position: 'bottom-right'
|
||||
type: 'error'
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -113,7 +126,9 @@ const submit = async (form: FormInstance, fn: () => void) => {
|
||||
fn(form).then(() => {})
|
||||
}
|
||||
const highlightCode = (json) => {
|
||||
if (json) {
|
||||
if (json.error) {
|
||||
successResponseBody.value = json.error.message
|
||||
} else if (json) {
|
||||
successResponseBody.value = hljs.lineNumbersValue(
|
||||
hljs.highlightAuto(JSON.stringify(json, null, 4), ['JSON']).value
|
||||
)
|
||||
@ -151,14 +166,16 @@ const submitEntitlement = async () => {
|
||||
}
|
||||
onBeforeMount(async () => {
|
||||
const route = useRoute()
|
||||
setOperationDetails(route.params.id)
|
||||
const version = route.query.version ? route.query.version : configVersion
|
||||
setOperationDetails(route.params.id, version)
|
||||
|
||||
const currentUser = await getCurrentUser()
|
||||
isUserLogon.value = currentUser.username
|
||||
setRoleForm()
|
||||
})
|
||||
onBeforeRouteUpdate((to) => {
|
||||
setOperationDetails(to.params.id)
|
||||
const version = to.query.version ? to.query.version : configVersion
|
||||
setOperationDetails(to.params.id, version)
|
||||
responseHeaderTitle.value = 'TYPICAL SUCCESSFUL RESPONSE'
|
||||
setRoleForm()
|
||||
})
|
||||
@ -183,7 +200,7 @@ onBeforeRouteUpdate((to) => {
|
||||
<input
|
||||
type="text"
|
||||
v-model="header"
|
||||
placeholder="Request Header (Heeader1:Value1::Header2:Value2)"
|
||||
placeholder="Request Header (Header1:Value1::Header2:Value2)"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex-preview-panel">
|
||||
@ -229,6 +246,7 @@ onBeforeRouteUpdate((to) => {
|
||||
</div>
|
||||
</el-form>
|
||||
<!--<div v-show="showValidations">-->
|
||||
<el-divider class="divider" />
|
||||
<div>
|
||||
<p>{{ $t('preview.validations') }}:</p>
|
||||
<!--TODO: implementation; replace hard coded.-->
|
||||
@ -239,6 +257,7 @@ onBeforeRouteUpdate((to) => {
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<el-divider class="divider" />
|
||||
<div v-show="showPossibleErrors">
|
||||
<p>{{ $t('preview.possible_errors') }}:</p>
|
||||
<ul>
|
||||
@ -247,6 +266,7 @@ onBeforeRouteUpdate((to) => {
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<el-divider class="divider" />
|
||||
<div v-show="showConnectorMethods">
|
||||
<p>{{ $t('preview.connector_methods') }}:</p>
|
||||
<ul>
|
||||
@ -255,6 +275,13 @@ onBeforeRouteUpdate((to) => {
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<el-divider class="divider" />
|
||||
<div>
|
||||
<p class="footnote">
|
||||
Version: {{ footNote.version }}, function_name: by {{ footNote.functionName }},
|
||||
operation_id: {{ footNote.functionName }}, Message Tags: {{ footNote.messageTags }}
|
||||
</p>
|
||||
</div>
|
||||
<br />
|
||||
</main>
|
||||
</template>
|
||||
@ -330,6 +357,15 @@ li {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
.footnote {
|
||||
color: var(--el-color-info);
|
||||
font-size: 12px;
|
||||
}
|
||||
.divider {
|
||||
border-top: 1px #253047 solid;
|
||||
margin-left: -25px;
|
||||
padding-right: 50px;
|
||||
}
|
||||
#search-input {
|
||||
-webkit-border-top-right-radius: 0;
|
||||
-moz-border-top-right-radius: 0;
|
||||
|
||||
@ -1,10 +1,13 @@
|
||||
<script lang="ts">
|
||||
import { reactive, ref, onBeforeMount, onMounted, inject } from 'vue'
|
||||
import { Search, Star } from '@element-plus/icons-vue'
|
||||
import { obpResourceDocsKey } from '@/obp/keys'
|
||||
import { Search } from '@element-plus/icons-vue'
|
||||
import { inject, onBeforeMount, onMounted, reactive, ref, watch } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { getMyAPICollections, getMyAPICollectionsEndpoint } from '../obp'
|
||||
|
||||
import { OBP_API_VERSION, getMyAPICollections, getMyAPICollectionsEndpoint } from '../obp'
|
||||
import { getGroupedResourceDocs } from '../obp/resource-docs'
|
||||
import { SEARCH_LINKS_COLOR as searchLinksColorSetting } from '../obp/style-setting'
|
||||
const operationIdTitle = {}
|
||||
const resourceDocs = ref({})
|
||||
const docs = ref({})
|
||||
const groups = ref({})
|
||||
const sortedKeys = ref([])
|
||||
@ -16,6 +19,7 @@ const form = reactive({
|
||||
const apiCollections = ref({})
|
||||
const apiCollectionsEndpointMapping = ref({})
|
||||
const apiCollectionsEndpoint = ref({})
|
||||
const searchLinksColor = ref(searchLinksColorSetting)
|
||||
|
||||
const clearActiveTab = () => {
|
||||
const activeTabs = document.querySelectorAll('.active-api-router-tab')
|
||||
@ -49,16 +53,39 @@ export const initializeAPICollections = async () => {
|
||||
|
||||
<script setup lang="ts">
|
||||
const route = useRoute()
|
||||
let selectedVersion = route.query.version ? route.query.version : `OBP${OBP_API_VERSION}`
|
||||
onBeforeMount(async () => {
|
||||
docs.value = inject('OBP-GroupedResourceDocs')!
|
||||
resourceDocs.value = inject(obpResourceDocsKey)!
|
||||
docs.value = getGroupedResourceDocs(selectedVersion, resourceDocs.value)
|
||||
groups.value = JSON.parse(JSON.stringify(docs.value))
|
||||
activeKeys.value = Object.keys(groups.value)
|
||||
sortedKeys.value = activeKeys.value.sort()
|
||||
await initializeAPICollections()
|
||||
setTabActive(route.params.id)
|
||||
let element = document.getElementById("selected-api-version")
|
||||
if (element !== null) {
|
||||
element.textContent = selectedVersion;
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
routeToFirstAPI()
|
||||
})
|
||||
|
||||
watch(
|
||||
() => route.query.version,
|
||||
async (version) => {
|
||||
selectedVersion = version
|
||||
docs.value = getGroupedResourceDocs(version, resourceDocs.value)
|
||||
groups.value = JSON.parse(JSON.stringify(docs.value))
|
||||
activeKeys.value = Object.keys(groups.value)
|
||||
sortedKeys.value = activeKeys.value.sort()
|
||||
await initializeAPICollections()
|
||||
routeToFirstAPI()
|
||||
}
|
||||
)
|
||||
|
||||
const routeToFirstAPI = () => {
|
||||
let element
|
||||
const elements = document.getElementsByClassName('api-router-link')
|
||||
const id = route.params.id
|
||||
@ -71,9 +98,9 @@ onMounted(() => {
|
||||
if (element) {
|
||||
element.click()
|
||||
} else {
|
||||
elements.item(0).click()
|
||||
if (elements.item(0)) elements.item(0).click()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const sortLinks = (items: any) => {
|
||||
const uniqueLinks = {}
|
||||
@ -103,20 +130,23 @@ const setActive = (event) => {
|
||||
}
|
||||
}
|
||||
|
||||
const isKeyFound = (keys, item) => keys.every((k) => item.toLowerCase().includes(k))
|
||||
|
||||
const filterKeys = (keys, key) => {
|
||||
const splitKey = key.split(' ').map((k) => k.toLowerCase())
|
||||
return keys.filter((title) => {
|
||||
const isGroupFound = title.toLowerCase().includes(key.toLowerCase())
|
||||
const items = docs.value[title].filter((item) =>
|
||||
item.summary.toLowerCase().includes(key.toLowerCase())
|
||||
const isGroupFound = isKeyFound(splitKey, title)
|
||||
const items = docs.value[title].filter(
|
||||
(item) => isGroupFound || isKeyFound(splitKey, item.summary)
|
||||
)
|
||||
groups.value[title] = items
|
||||
return isGroupFound || items.length > 0
|
||||
})
|
||||
}
|
||||
|
||||
const searchEvent = (event) => {
|
||||
if (event) {
|
||||
sortedKeys.value = filterKeys(activeKeys.value, event)
|
||||
const searchEvent = (value) => {
|
||||
if (value) {
|
||||
sortedKeys.value = filterKeys(activeKeys.value, value)
|
||||
} else {
|
||||
groups.value = JSON.parse(JSON.stringify(docs.value))
|
||||
sortedKeys.value = Object.keys(groups.value).sort()
|
||||
@ -127,56 +157,27 @@ const searchEvent = (event) => {
|
||||
<template>
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-input
|
||||
v-model="form.search"
|
||||
placeholder="Search"
|
||||
:prefix-icon="Search"
|
||||
@input="searchEvent"
|
||||
/>
|
||||
<el-input v-model="form.search" placeholder="Search" :prefix-icon="Search" @input="searchEvent" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-collapse v-model="activeKeys">
|
||||
<el-collapse-item title="My Collections" v-show="showMyCollections" name="my-collections">
|
||||
<el-collapse-item
|
||||
v-for="(api, key) of apiCollections"
|
||||
:key="key"
|
||||
:title="api.api_collection_name"
|
||||
:name="api.api_collection_name"
|
||||
class="child-collapse"
|
||||
>
|
||||
<el-collapse-item v-for="(api, key) of apiCollections" :key="key" :title="api.api_collection_name"
|
||||
:name="api.api_collection_name" class="child-collapse">
|
||||
<div class="el-tabs--right">
|
||||
<div
|
||||
v-for="(value, key) of apiCollectionsEndpoint[api.api_collection_name]"
|
||||
:key="key"
|
||||
class="api-router-tab"
|
||||
@click="setActive"
|
||||
>
|
||||
<RouterLink
|
||||
:to="{ name: 'api', params: { id: value } }"
|
||||
:id="value"
|
||||
active-class="active-api-router-link"
|
||||
class="api-router-link"
|
||||
>{{ operationIdTitle[value] }}</RouterLink
|
||||
>
|
||||
<div v-for="(value, key) of apiCollectionsEndpoint[api.api_collection_name]" :key="key" class="api-router-tab"
|
||||
@click="setActive">
|
||||
<RouterLink :to="{ name: 'api', params: { id: value }, query: { version: selectedVersion } }" :id="value"
|
||||
active-class="active-api-router-link" class="api-router-link">{{ operationIdTitle[value] }}</RouterLink>
|
||||
</div>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item v-for="key in sortedKeys" :title="key" :key="key" :name="key">
|
||||
<div class="el-tabs--right">
|
||||
<div
|
||||
v-for="(value, key) of sortLinks(groups[key])"
|
||||
:key="value"
|
||||
class="api-router-tab"
|
||||
@click="setActive"
|
||||
>
|
||||
<RouterLink
|
||||
active-class="active-api-router-link"
|
||||
class="api-router-link"
|
||||
:id="value"
|
||||
:to="{ name: 'api', params: { id: value } }"
|
||||
>{{ key }}</RouterLink
|
||||
>
|
||||
<div v-for="(value, key) of sortLinks(groups[key])" :key="value" class="api-router-tab" @click="setActive">
|
||||
<RouterLink active-class="active-api-router-link" class="api-router-link" :id="value"
|
||||
:to="{ name: 'api', params: { id: value }, query: { version: selectedVersion } }">{{ key }}</RouterLink>
|
||||
</div>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
@ -206,13 +207,14 @@ const searchEvent = (event) => {
|
||||
|
||||
.api-router-tab:hover,
|
||||
.active-api-router-tab {
|
||||
border-left: 2px solid #52b165;
|
||||
border-left: 2px solid v-bind(searchLinksColor);
|
||||
}
|
||||
|
||||
.api-router-tab:hover .api-router-link,
|
||||
.active-api-router-link {
|
||||
color: #52b165;
|
||||
color: v-bind(searchLinksColor);
|
||||
}
|
||||
|
||||
.favorite {
|
||||
cursor: pointer;
|
||||
line-height: 2;
|
||||
@ -222,6 +224,7 @@ const searchEvent = (event) => {
|
||||
padding: 12px;
|
||||
color: #39455f;
|
||||
}
|
||||
|
||||
.child-collapse {
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
{
|
||||
"header": {
|
||||
"portal_home": "Portal Home",
|
||||
"portal_home": "Portal Home",
|
||||
"api_explorer": "API Explorer",
|
||||
"api_manager": "API Manager",
|
||||
|
||||
144
src/main.ts
144
src/main.ts
@ -3,12 +3,13 @@ import { createPinia } from 'pinia'
|
||||
import ElementPlus from 'element-plus'
|
||||
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
import appRouter from './router'
|
||||
import { createI18n } from 'vue-i18n'
|
||||
import { languages, defaultLocale } from './language'
|
||||
|
||||
import { getOBPResourceDocs, getGroupedResourceDocs } from './obp/resource-docs'
|
||||
import { getMyAPICollections, getMyAPICollectionsEndpoint } from './obp'
|
||||
import { cache as cacheResourceDocs, cacheDoc as cacheResourceDocsDoc } from './obp/resource-docs'
|
||||
import { cache as cacheMessageDocs, cacheDoc as cacheMessageDocsDoc } from './obp/message-docs'
|
||||
import { OBP_API_VERSION, getMyAPICollections, getMyAPICollectionsEndpoint } from './obp'
|
||||
import { getOBPGlossary } from './obp/glossary'
|
||||
|
||||
import 'element-plus/dist/index.css'
|
||||
@ -16,47 +17,108 @@ import './assets/main.css'
|
||||
import '@fontsource/roboto/300.css'
|
||||
import '@fontsource/roboto/400.css'
|
||||
import '@fontsource/roboto/700.css'
|
||||
;(async () => {
|
||||
import { obpApiActiveVersionsKey, obpApiHostKey, obpGlossaryKey, obpGroupedMessageDocsKey, obpGroupedResourceDocsKey, obpMyCollectionsEndpointKey, obpResourceDocsKey } from './obp/keys'
|
||||
import { getCacheStorageInfo } from './obp/common-functions'
|
||||
(async () => {
|
||||
const app = createApp(App)
|
||||
const router = await appRouter()
|
||||
try {
|
||||
const worker = new Worker('/js/worker/web-worker.js')
|
||||
const isDataSetup = await setupData(app, worker)
|
||||
|
||||
const cache = await caches.open('obp-resource-docs-cache')
|
||||
const response = await cache.match('/operationid')
|
||||
let docs
|
||||
if (response) {
|
||||
docs = await response.json()
|
||||
} else {
|
||||
docs = await getOBPResourceDocs()
|
||||
await cache.put('/operationid', new Response(JSON.stringify(docs)))
|
||||
}
|
||||
const groupedDocs = await getGroupedResourceDocs(docs)
|
||||
app.provide('OBP-ResourceDocs', docs)
|
||||
app.provide('OBP-GroupedResourceDocs', groupedDocs)
|
||||
app.provide('OBP-API-Host', import.meta.env.VITE_OBP_API_HOST)
|
||||
const glossary = await getOBPGlossary()
|
||||
app.provide('OBP-Glossary', glossary)
|
||||
const messages = Object.assign(languages)
|
||||
const i18n = createI18n({
|
||||
locale: defaultLocale,
|
||||
fallbackLocale: 'ES',
|
||||
messages
|
||||
})
|
||||
app.provide('i18n', i18n)
|
||||
|
||||
const apiCollections = (await getMyAPICollections()).api_collections
|
||||
if (apiCollections) {
|
||||
for (const { api_collection_name } of apiCollections) {
|
||||
const apiCollectionsEndpoint = (
|
||||
await getMyAPICollectionsEndpoint(api_collection_name)
|
||||
).api_collection_endpoints.map((api) => api.operation_id)
|
||||
app.provide('OBP-MyCollectionsEndpoint', apiCollectionsEndpoint)
|
||||
app.use(ElementPlus)
|
||||
app.use(i18n)
|
||||
app.use(createPinia())
|
||||
app.use(router)
|
||||
|
||||
app.mount('#app')
|
||||
|
||||
if (!isDataSetup) router.replace({ path: 'api-server-error' })
|
||||
app.config.errorHandler = (error) => {
|
||||
console.log(error)
|
||||
router.replace({ path: 'error' })
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
router.replace({ path: 'error' })
|
||||
}
|
||||
|
||||
const messages = Object.assign(languages)
|
||||
const i18n = createI18n({
|
||||
locale: defaultLocale,
|
||||
fallbackLocale: 'ES',
|
||||
messages
|
||||
})
|
||||
app.provide('i18n', i18n)
|
||||
|
||||
app.use(ElementPlus)
|
||||
app.use(i18n)
|
||||
app.use(createPinia())
|
||||
app.use(router)
|
||||
|
||||
app.mount('#app')
|
||||
})()
|
||||
|
||||
async function setupData(app: App<Element>, worker: Worker) {
|
||||
try {
|
||||
// 'open': Returns a Promise that resolves to the Cache object matching the cacheName(obp-resource-docs-cache) (a new cache is created if it doesn't already exist.)
|
||||
const cacheStorageOfResourceDocs = await caches.open('obp-resource-docs-cache') // Please note: The global 'caches' read-only property returns the 'CacheStorage' object associated with the current context.
|
||||
// 'match': Checks if a given Request is a key in any of the Cache objects that the CacheStorage object tracks, and returns a Promise that resolves to that match.
|
||||
const cachedResponseOfResourceDocs = await cacheStorageOfResourceDocs.match('/')
|
||||
// 'open': Returns a Promise that resolves to the Cache object matching the cacheName(obp-message-docs-cache) (a new cache is created if it doesn't already exist.)
|
||||
const cacheStorageOfMessageDocs = await caches.open('obp-message-docs-cache') // Please note: The global 'caches' read-only property returns the 'CacheStorage' object associated with the current context.
|
||||
// 'match': Checks if a given Request is a key in any of the Cache objects that the CacheStorage object tracks, and returns a Promise that resolves to that match.
|
||||
const cachedResponseOfMessageDocs = await cacheStorageOfMessageDocs.match('/')
|
||||
|
||||
// Listen to Web worker
|
||||
worker.onmessage = async (event) => {
|
||||
// Update cache docs data in the background
|
||||
if (event.data === 'update-resource-docs') {
|
||||
await cacheResourceDocsDoc(cacheStorageOfResourceDocs)
|
||||
console.log('Resource Docs cache was updated.')
|
||||
const storageInfo = await getCacheStorageInfo()
|
||||
console.log(storageInfo)
|
||||
}
|
||||
if (event.data === 'update-message-docs') {
|
||||
await cacheMessageDocsDoc(cacheStorageOfMessageDocs)
|
||||
console.log('Message Docs cache was updated.')
|
||||
}
|
||||
}
|
||||
|
||||
const { resourceDocs, groupedDocs } = await cacheResourceDocs(
|
||||
cacheStorageOfResourceDocs,
|
||||
cachedResponseOfResourceDocs,
|
||||
worker
|
||||
)
|
||||
const messageDocs = await cacheMessageDocs(
|
||||
cacheStorageOfMessageDocs,
|
||||
cachedResponseOfMessageDocs,
|
||||
worker
|
||||
)
|
||||
|
||||
// Provide data to a component's descendants
|
||||
// App-level provides are available to all components rendered in the app
|
||||
// Info: https://vuejs.org/guide/components/provide-inject.html
|
||||
app.provide(obpResourceDocsKey, resourceDocs)
|
||||
app.provide(obpApiActiveVersionsKey, Object.keys(resourceDocs).sort())
|
||||
app.provide(obpGroupedResourceDocsKey, groupedDocs)
|
||||
app.provide(obpGroupedMessageDocsKey, messageDocs)
|
||||
app.provide(obpApiHostKey, import.meta.env.VITE_OBP_API_HOST)
|
||||
const glossary = await getOBPGlossary()
|
||||
app.provide(obpGlossaryKey, glossary)
|
||||
|
||||
const apiCollections = (await getMyAPICollections()).api_collections
|
||||
if (apiCollections && apiCollections.length > 0) {
|
||||
//Uncomment this when other collection will be supported.
|
||||
//for (const { api_collection_name } of apiCollections) {
|
||||
// const apiCollectionsEndpoint = (
|
||||
// await getMyAPICollectionsEndpoint(api_collection_name)
|
||||
// ).api_collection_endpoints.map((api) => api.operation_id)
|
||||
// app.provide(obpMyCollectionsEndpointKey, apiCollectionsEndpoint)
|
||||
//}
|
||||
const apiCollectionsEndpoint = (
|
||||
await getMyAPICollectionsEndpoint('Favourites')
|
||||
).api_collection_endpoints.map((api) => api.operation_id)
|
||||
app.provide(obpMyCollectionsEndpointKey, apiCollectionsEndpoint)
|
||||
} else {
|
||||
app.provide(obpMyCollectionsEndpointKey, undefined)
|
||||
}
|
||||
return true
|
||||
} catch (error) {
|
||||
app.provide(obpApiActiveVersionsKey, [OBP_API_VERSION])
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
6
src/obp/api-version.ts
Normal file
6
src/obp/api-version.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { OBP_API_VERSION, get } from '../obp'
|
||||
|
||||
// Get API Versions
|
||||
export async function getOBPAPIVersions(): Promise<any> {
|
||||
return await get(`obp/${OBP_API_VERSION}/api/versions`)
|
||||
}
|
||||
44
src/obp/common-functions.ts
Normal file
44
src/obp/common-functions.ts
Normal file
@ -0,0 +1,44 @@
|
||||
import { isServerUp, serverStatus } from '.'
|
||||
|
||||
export function updateLoadingInfoMessage(logMessage: string) {
|
||||
// 1. Select the div element using the id property
|
||||
const spinner = document.getElementById('loading-api-spinner')
|
||||
// 2. Select the div element using the id property
|
||||
let p = document.getElementById('loading-api-info')
|
||||
// 3. Add the text content
|
||||
if (p !== null) {
|
||||
p.textContent = logMessage
|
||||
} else {
|
||||
p = document.createElement('p')
|
||||
}
|
||||
// 4. Append the p element to the div element
|
||||
spinner?.appendChild(p)
|
||||
}
|
||||
|
||||
export function updateServerStatus() {
|
||||
const oElem = document.getElementById('backend-status')
|
||||
serverStatus()
|
||||
.then((body) => {
|
||||
if (oElem) {
|
||||
Object.values(body).every((i) => i === true)
|
||||
? (oElem.className = 'server-is-online')
|
||||
: (oElem.className = 'server-is-offline')
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error)
|
||||
if (oElem) {
|
||||
oElem.className = 'server-is-offline'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export async function getCacheStorageInfo() {
|
||||
const message = await navigator.storage.estimate().then((estimate) => {
|
||||
const percent = ((estimate.usage / estimate.quota) * 100).toFixed(2)
|
||||
const quota = (estimate.quota / 1024 / 1024).toFixed(2) + 'MB'
|
||||
const message = `You're currently using about ${percent}% of your estimated storage quota ${quota}`
|
||||
return message
|
||||
}).catch((error) => {return `Cannot estimate Cache Storage quota. ${error}`})
|
||||
return message
|
||||
}
|
||||
@ -1,13 +1,10 @@
|
||||
import { Any, GetAny, Version, API, get } from 'obp-typescript'
|
||||
import type { APIClientConfig } from 'obp-typescript'
|
||||
|
||||
const clientConfig: APIClientConfig = {
|
||||
baseUri: import.meta.env.VITE_OBP_API_HOST,
|
||||
version: Version.v510,
|
||||
withFixedVersion: true
|
||||
}
|
||||
import { OBP_API_VERSION, get } from '../obp'
|
||||
import { updateLoadingInfoMessage } from './common-functions'
|
||||
|
||||
// Get Glossary
|
||||
export async function getOBPGlossary(): Promise<any> {
|
||||
return await get<API.Any>(clientConfig, Any)(GetAny)('/api/glossary')
|
||||
const logMessage = `Loading glossary { version: ${OBP_API_VERSION} }`
|
||||
console.log(logMessage)
|
||||
updateLoadingInfoMessage(logMessage)
|
||||
return await get(`obp/${OBP_API_VERSION}/api/glossary`)
|
||||
}
|
||||
|
||||
@ -1,15 +1,23 @@
|
||||
import superagent from 'superagent'
|
||||
import { Version } from 'obp-typescript'
|
||||
|
||||
const version = import.meta.env.VITE_OBP_API_VERSION
|
||||
export const OBP_API_VERSION = import.meta.env.VITE_OBP_API_VERSION
|
||||
const default_collection_name = 'Favourites'
|
||||
|
||||
export async function serverStatus(): Promise<any> {
|
||||
return (await superagent.get(`/api/status`)).body
|
||||
}
|
||||
|
||||
export async function isServerUp(): Promise<boolean> {
|
||||
//Set the status to offline/down only if all the resource data is not availalbe.
|
||||
return !Object.values(await serverStatus()).every((isTrue) => !isTrue)
|
||||
}
|
||||
|
||||
export async function get(path: string): Promise<any> {
|
||||
try {
|
||||
return (await superagent.get(`/api/get?path=${path}`)).body
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
return error
|
||||
return { error }
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,7 +26,7 @@ export async function create(path: string, body: any): Promise<any> {
|
||||
return (await superagent.post(`/api/create?path=${path}`).send(JSON.parse(body))).body
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
return error
|
||||
return { error }
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,7 +35,7 @@ export async function update(path: string, body: any): Promise<any> {
|
||||
return (await superagent.put(`/api/update?path=${path}`).send(JSON.parse(body))).body
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
return error
|
||||
return { error }
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,7 +44,7 @@ export async function discard(path: string): Promise<any> {
|
||||
return (await superagent.delete(`/api/delete?path=${path}`)).body
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
return error
|
||||
return { error }
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,12 +53,13 @@ export async function getCurrentUser(): Promise<any> {
|
||||
return (await superagent.get(`/api/user/current`)).body
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
return { error }
|
||||
}
|
||||
}
|
||||
|
||||
export async function createEntitlement(bankId: string, roleName: string): Promise<any> {
|
||||
const userId = (await getCurrentUser()).user_id
|
||||
const url = `/obp/${version}/users/${userId}/entitlements`
|
||||
const url = `/obp/${OBP_API_VERSION}/users/${userId}/entitlements`
|
||||
const body = {
|
||||
role_name: roleName,
|
||||
bank_id: bankId
|
||||
@ -59,7 +68,7 @@ export async function createEntitlement(bankId: string, roleName: string): Promi
|
||||
}
|
||||
|
||||
export async function createMyAPICollection(): Promise<any> {
|
||||
const url = `/obp/${version}/my/api-collections`
|
||||
const url = `/obp/${OBP_API_VERSION}/my/api-collections`
|
||||
const body = {
|
||||
api_collection_name: default_collection_name,
|
||||
is_sharable: true
|
||||
@ -68,7 +77,7 @@ export async function createMyAPICollection(): Promise<any> {
|
||||
}
|
||||
|
||||
export async function createMyAPICollectionEndpoint(operation_id: string): Promise<any> {
|
||||
const url = `/obp/${version}/my/api-collections/${default_collection_name}/api-collection-endpoints`
|
||||
const url = `/obp/${OBP_API_VERSION}/my/api-collections/${default_collection_name}/api-collection-endpoints`
|
||||
const body = {
|
||||
operation_id
|
||||
}
|
||||
@ -76,14 +85,14 @@ export async function createMyAPICollectionEndpoint(operation_id: string): Promi
|
||||
}
|
||||
|
||||
export async function deleteMyAPICollectionEndpoint(operation_id: string): Promise<any> {
|
||||
const url = `/obp/${version}/my/api-collections/${default_collection_name}/api-collection-endpoints/${operation_id}`
|
||||
const url = `/obp/${OBP_API_VERSION}/my/api-collections/${default_collection_name}/api-collection-endpoints/${operation_id}`
|
||||
return await discard(url)
|
||||
}
|
||||
|
||||
export async function getMyAPICollections(): Promise<any> {
|
||||
return await get(`/obp/${version}/my/api-collections`)
|
||||
return await get(`/obp/${OBP_API_VERSION}/my/api-collections`)
|
||||
}
|
||||
|
||||
export async function getMyAPICollectionsEndpoint(collectionName: string): Promise<any> {
|
||||
return await get(`/obp/${version}/my/api-collections/${collectionName}/api-collection-endpoints`)
|
||||
return await get(`/obp/${OBP_API_VERSION}/my/api-collections/${collectionName}/api-collection-endpoints`)
|
||||
}
|
||||
|
||||
9
src/obp/keys.ts
Normal file
9
src/obp/keys.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import type { InjectionKey } from 'vue'
|
||||
|
||||
export const obpResourceDocsKey = Symbol('OBP-ResourceDocs') as InjectionKey<any>
|
||||
export const obpApiActiveVersionsKey = Symbol('OBP-APIActiveVersions') as InjectionKey<any>
|
||||
export const obpGroupedResourceDocsKey = Symbol('OBP-GroupedResourceDocs') as InjectionKey<any>
|
||||
export const obpGroupedMessageDocsKey = Symbol('OBP-GroupedMessageDocs') as InjectionKey<any> // This cause an issue
|
||||
export const obpApiHostKey = Symbol('OBP-API-Host') as InjectionKey<any>
|
||||
export const obpGlossaryKey = Symbol('OBP-Glossary') as InjectionKey<any>
|
||||
export const obpMyCollectionsEndpointKey = Symbol('OBP-MyCollectionsEndpoint') as InjectionKey<any>
|
||||
62
src/obp/message-docs.ts
Normal file
62
src/obp/message-docs.ts
Normal file
@ -0,0 +1,62 @@
|
||||
import { OBP_API_VERSION, get, isServerUp } from '../obp'
|
||||
import { updateLoadingInfoMessage } from './common-functions'
|
||||
|
||||
export const connectors = [
|
||||
'kafka_vSept2018',
|
||||
'akka_vDec2018',
|
||||
'rest_vMar2019',
|
||||
'stored_procedure_vDec2019'
|
||||
]
|
||||
|
||||
// Get Message Docs
|
||||
export async function getOBPMessageDocs(item: string): Promise<any> {
|
||||
const logMessage = `Loading message docs { connector: ${item} }`
|
||||
console.log(logMessage)
|
||||
updateLoadingInfoMessage(logMessage)
|
||||
return await get(`obp/${OBP_API_VERSION}/message-docs/${item}`)
|
||||
}
|
||||
|
||||
export function getGroupedMessageDocs(docs: any): Promise<any> {
|
||||
return docs.message_docs.reduce((values: any, doc: any) => {
|
||||
const tag = doc.adapter_implementation.group.replace('-', '').trim()
|
||||
;(values[tag] = values[tag] || []).push(doc)
|
||||
return values
|
||||
}, {})
|
||||
}
|
||||
|
||||
export async function cacheDoc(cacheStorageOfMessageDocs: any): Promise<any> {
|
||||
const messageDocs = await connectors.reduce(async (agroup: any, connector: any) => {
|
||||
const logMessage = `Caching message docs { connector: ${connector} }`
|
||||
console.log(logMessage)
|
||||
updateLoadingInfoMessage(logMessage)
|
||||
const group = await agroup
|
||||
const docs = await getOBPMessageDocs(connector)
|
||||
if (!Object.keys(docs).includes('code')) {
|
||||
group[connector] = getGroupedMessageDocs(docs)
|
||||
}
|
||||
return group
|
||||
}, Promise.resolve({}))
|
||||
await cacheStorageOfMessageDocs.put('/', new Response(JSON.stringify(messageDocs)))
|
||||
return messageDocs
|
||||
}
|
||||
|
||||
async function getCacheDoc(cacheStorageOfMessageDocs: any): Promise<any> {
|
||||
return await cacheDoc(cacheStorageOfMessageDocs)
|
||||
}
|
||||
|
||||
export async function cache(
|
||||
cacheStorage: any,
|
||||
cachedResponse: any,
|
||||
worker: any
|
||||
): Promise<any> {
|
||||
try {
|
||||
worker.postMessage('update-message-docs')
|
||||
return await cachedResponse.json()
|
||||
} catch (error) {
|
||||
console.warn('No message docs cache or malformed cache.')
|
||||
console.log('Caching message docs...')
|
||||
const isServerActive = await isServerUp()
|
||||
if (!isServerActive) throw new Error('API Server is not responding.')
|
||||
return await getCacheDoc(cacheStorage)
|
||||
}
|
||||
}
|
||||
@ -1,26 +1,75 @@
|
||||
import { Any, GetAny, Version, API, get } from 'obp-typescript'
|
||||
import type { APIClientConfig } from 'obp-typescript'
|
||||
|
||||
const clientConfig: APIClientConfig = {
|
||||
baseUri: import.meta.env.VITE_OBP_API_HOST,
|
||||
version: Version.v510,
|
||||
withFixedVersion: true
|
||||
}
|
||||
import { OBP_API_VERSION, get, isServerUp } from '../obp'
|
||||
import { getOBPAPIVersions } from '../obp/api-version'
|
||||
import { updateLoadingInfoMessage } from './common-functions'
|
||||
|
||||
// Get Resource Docs
|
||||
export async function getOBPResourceDocs(): Promise<any> {
|
||||
return await get<API.Any>(clientConfig, Any)(GetAny)(`/resource-docs/${Version.v510}/obp`)
|
||||
export async function getOBPResourceDocs(apiStandardAndVersion: string): Promise<any> {
|
||||
const logMessage = `Loading API ${apiStandardAndVersion}`
|
||||
console.log(logMessage)
|
||||
updateLoadingInfoMessage(logMessage)
|
||||
return await get(`/obp/${OBP_API_VERSION}/resource-docs/${apiStandardAndVersion}/obp`)
|
||||
}
|
||||
|
||||
export async function getGroupedResourceDocs(docs: any): Promise<any> {
|
||||
return docs.resource_docs.reduce((values: any, doc: any) => {
|
||||
doc.tags.forEach((tag: any) => {
|
||||
;(values[tag] = values[tag] || []).push(doc)
|
||||
})
|
||||
export function getGroupedResourceDocs(apiStandardAndVersion: string, docs: any): Promise<any> {
|
||||
if (apiStandardAndVersion === undefined || docs === undefined) return Promise.resolve<any>({})
|
||||
|
||||
return docs[apiStandardAndVersion].resource_docs.reduce((values: any, doc: any) => {
|
||||
const tag = doc.tags[0] // Group by the first tag at resorce doc
|
||||
;(values[tag] = values[tag] || []).push(doc)
|
||||
return values
|
||||
}, {})
|
||||
}
|
||||
|
||||
export function getOperationDetails(docs: any, operation_id: string): any {
|
||||
return docs.resource_docs.filter((doc: any) => doc.operation_id === operation_id)[0]
|
||||
export function getOperationDetails(version: string, operation_id: string, docs: any): any {
|
||||
return docs[version].resource_docs.filter((doc: any) => doc.operation_id === operation_id)[0]
|
||||
}
|
||||
|
||||
export async function cacheDoc(cacheStorageOfResourceDocs: any): Promise<any> {
|
||||
const apiVersions = await getOBPAPIVersions()
|
||||
if (apiVersions) {
|
||||
const scannedAPIVersions = apiVersions.scanned_api_versions
|
||||
const resourceDocsMapping: any = {}
|
||||
for (const { apiStandard, API_VERSION } of scannedAPIVersions) {
|
||||
const logMessage = `Caching API { standard: ${apiStandard}, version: ${API_VERSION} }`
|
||||
console.log(logMessage)
|
||||
if (apiStandard) {
|
||||
const version = `${apiStandard.toUpperCase()}${API_VERSION}`
|
||||
const resourceDocs = await getOBPResourceDocs(version)
|
||||
if (version && Object.keys(resourceDocs).includes('resource_docs'))
|
||||
resourceDocsMapping[version] = resourceDocs
|
||||
}
|
||||
updateLoadingInfoMessage(logMessage)
|
||||
}
|
||||
await cacheStorageOfResourceDocs.put('/', new Response(JSON.stringify(resourceDocsMapping)))
|
||||
return resourceDocsMapping
|
||||
} else {
|
||||
const resourceDocs = { ['OBP' + OBP_API_VERSION]: await getOBPResourceDocs(OBP_API_VERSION) }
|
||||
await cacheStorageOfResourceDocs.put('/', new Response(JSON.stringify(resourceDocs)))
|
||||
return resourceDocs
|
||||
}
|
||||
}
|
||||
|
||||
async function getCacheDoc(cacheStorageOfResourceDocs: any): Promise<any> {
|
||||
return await cacheDoc(cacheStorageOfResourceDocs)
|
||||
}
|
||||
|
||||
export async function cache(
|
||||
cachedStorage: any,
|
||||
cachedResponse: any,
|
||||
worker: any
|
||||
): Promise<any> {
|
||||
try {
|
||||
worker.postMessage('update-resource-docs')
|
||||
const resourceDocs = await cachedResponse.json()
|
||||
const groupedResourceDocs = getGroupedResourceDocs('OBP' + OBP_API_VERSION, resourceDocs)
|
||||
return { resourceDocs, groupedDocs: groupedResourceDocs }
|
||||
} catch (error) {
|
||||
console.warn('No resource docs cache or malformed cache.')
|
||||
console.log('Caching resource docs...')
|
||||
const isServerActive = await isServerUp()
|
||||
if (!isServerActive) throw new Error('API Server is not responding.')
|
||||
const resourceDocs = await getCacheDoc(cachedStorage)
|
||||
const groupedDocs = getGroupedResourceDocs('OBP' + OBP_API_VERSION, resourceDocs)
|
||||
return { resourceDocs, groupedDocs }
|
||||
}
|
||||
}
|
||||
|
||||
14
src/obp/style-setting.ts
Normal file
14
src/obp/style-setting.ts
Normal file
@ -0,0 +1,14 @@
|
||||
//Logo
|
||||
export const LOGO_URL = import.meta.env.VITE_OBP_LOGO_URL
|
||||
|
||||
//Header styles
|
||||
export const HEADER_LINKS_COLOR = import.meta.env.VITE_OBP_HEADER_LINKS_COLOR || '#39455f'
|
||||
export const HEADER_LINKS_HOVER_COLOR = import.meta.env.VITE_OBP_HEADER_LINKS_HOVER_COLOR || '#39455f'
|
||||
export const HEADER_LINKS_BACKGROUND_COLOR =
|
||||
import.meta.env.VITE_OBP_HEADER_LINKS_BACKGROUND_COLOR || '#eef0f4'
|
||||
|
||||
//Search nav styles
|
||||
export const SEARCH_LINKS_COLOR = import.meta.env.VITE_OBP_LINKS_COLOR || '#52b165'
|
||||
|
||||
//Content summary styles
|
||||
export const SUMMARY_PAGER_LINKS_COLOR = import.meta.env.VITE_OBP_LINKS_COLOR || '#52b165'
|
||||
@ -1,50 +1,69 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import GlossaryView from '../views/GlossaryView.vue'
|
||||
import MessageDocsView from '../views/MessageDocsView.vue'
|
||||
import BodyView from '../views/BodyView.vue'
|
||||
import Content from '../components/Content.vue'
|
||||
import Preview from '../components/Preview.vue'
|
||||
import NotFoundView from '../views/NotFoundView.vue'
|
||||
import InternalServerErrorView from '../views/InternalServerErrorView.vue'
|
||||
import APIServerErrorView from '../views/APIServerErrorView.vue'
|
||||
import APIServerStatusView from '../views/APIServerStatusView.vue'
|
||||
import { isServerUp } from '../obp'
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
mode: 'history',
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
redirect: '/operationid'
|
||||
},
|
||||
{
|
||||
path: '/glossary',
|
||||
name: 'glossary',
|
||||
component: GlossaryView
|
||||
},
|
||||
{
|
||||
path: '/operationid',
|
||||
name: 'operationid',
|
||||
component: BodyView
|
||||
},
|
||||
{
|
||||
path: '/operationid/:id',
|
||||
name: 'operationid-path',
|
||||
component: BodyView,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'api',
|
||||
components: {
|
||||
body: Content,
|
||||
preview: Preview
|
||||
export default async function router(): Promise<any> {
|
||||
const isServerActive = await isServerUp()
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
mode: 'history',
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
redirect: isServerActive ? '/operationid' : '/api-server-error'
|
||||
},
|
||||
{
|
||||
path: '/status',
|
||||
name: 'status',
|
||||
component: APIServerStatusView
|
||||
},
|
||||
{
|
||||
path: '/glossary',
|
||||
name: 'glossary',
|
||||
component: isServerActive ? GlossaryView : InternalServerErrorView
|
||||
},
|
||||
{
|
||||
path: '/message-docs/:id',
|
||||
name: 'message-docs',
|
||||
component: isServerActive ? MessageDocsView : InternalServerErrorView
|
||||
},
|
||||
{
|
||||
path: '/operationid',
|
||||
name: 'operationid',
|
||||
component: isServerActive ? BodyView : InternalServerErrorView
|
||||
},
|
||||
{
|
||||
path: '/operationid/:id',
|
||||
name: 'operationid-path',
|
||||
component: BodyView,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'api',
|
||||
components: {
|
||||
body: Content,
|
||||
preview: Preview
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/callback',
|
||||
name: 'callback',
|
||||
component: BodyView
|
||||
},
|
||||
{ path: '/:pathMatch(.*)*', name: 'notFound', component: NotFoundView }
|
||||
]
|
||||
})
|
||||
|
||||
export default router
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/callback',
|
||||
name: 'callback',
|
||||
component: isServerActive ? BodyView : InternalServerErrorView
|
||||
},
|
||||
{ path: '/error', name: 'error', component: InternalServerErrorView },
|
||||
{ path: '/api-server-error', name: 'apiServerError', component: APIServerErrorView },
|
||||
{ path: '/:pathMatch(.*)*', name: 'notFound', component: NotFoundView }
|
||||
]
|
||||
})
|
||||
return router
|
||||
}
|
||||
|
||||
27
src/views/APIServerErrorView.vue
Normal file
27
src/views/APIServerErrorView.vue
Normal file
@ -0,0 +1,27 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
const version = ref(__APP_VERSION__)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main>500 | The API server is not responding.</main>
|
||||
<span>Version: {{ version }}</span>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
main {
|
||||
margin-top: -60px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: #39455f;
|
||||
font-family: 'roboto';
|
||||
font-size: 30px;
|
||||
}
|
||||
span {
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
81
src/views/APIServerStatusView.vue
Normal file
81
src/views/APIServerStatusView.vue
Normal file
@ -0,0 +1,81 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onBeforeMount } from 'vue'
|
||||
import { SuccessFilled, RemoveFilled } from '@element-plus/icons-vue'
|
||||
import { serverStatus } from './../obp'
|
||||
const version = ref(__APP_VERSION__)
|
||||
const status = ref({})
|
||||
onBeforeMount(async () => {
|
||||
status.value = await serverStatus()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main>
|
||||
<div class="content">
|
||||
<div v-for="(value, name, index) in status">
|
||||
<div v-if="name === 'status'" class="status">
|
||||
<el-icon v-if="value === true" style="vertical-align: middle; color: green; width: auto"
|
||||
><SuccessFilled
|
||||
/></el-icon>
|
||||
<el-icon v-else style="vertical-align: middle; color: red"><RemoveFilled /></el-icon>
|
||||
<span class="main-status-label">{{ name }}</span>
|
||||
</div>
|
||||
<div v-else class="sub-status">
|
||||
<span class="status-label">{{ name }}</span>
|
||||
<el-divider />
|
||||
<el-icon
|
||||
v-if="value === true"
|
||||
style="vertical-align: middle; color: green; font-size: 16px"
|
||||
><SuccessFilled
|
||||
/></el-icon>
|
||||
<el-icon v-else style="vertical-align: middle; color: red"><RemoveFilled /></el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<span>Version: {{ version }}</span>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
main {
|
||||
margin-top: -60px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: #39455f;
|
||||
font-family: 'roboto';
|
||||
font-size: 30px;
|
||||
}
|
||||
span {
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.content {
|
||||
width: 30%;
|
||||
}
|
||||
.main-status-label {
|
||||
display: block;
|
||||
font-size: 28px;
|
||||
padding: 15px;
|
||||
}
|
||||
.main-status-label:first-letter {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.status-label {
|
||||
font-size: 16px;
|
||||
}
|
||||
.status {
|
||||
display: inline-grid;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
.sub-status {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
@ -1,6 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import SearchNav from '../components/SearchNav.vue'
|
||||
import Menu from '../components/Menu.vue'
|
||||
import Collections from '../components/Collections.vue'
|
||||
import { inject } from 'vue'
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -11,6 +14,9 @@ import Menu from '../components/Menu.vue'
|
||||
</el-aside>
|
||||
<el-main>
|
||||
<el-container class="main">
|
||||
<!--<el-header class="collections">
|
||||
<Collections />
|
||||
</el-header>-->
|
||||
<el-header class="menu">
|
||||
<Menu />
|
||||
</el-header>
|
||||
@ -49,4 +55,10 @@ import Menu from '../components/Menu.vue'
|
||||
background-color: #151d30;
|
||||
max-height: 100vh;
|
||||
}
|
||||
.collections {
|
||||
margin-left: -20px;
|
||||
margin-right: -20px;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref, onBeforeMount, onMounted, inject } from 'vue'
|
||||
import SearchNav from '../components/GlossarySearchNav.vue'
|
||||
import { obpGlossaryKey } from '@/obp/keys';
|
||||
|
||||
const glossary = ref(inject('OBP-Glossary')!.glossary_items)
|
||||
const glossary = ref(inject(obpGlossaryKey)!.glossary_items)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
27
src/views/InternalServerErrorView.vue
Normal file
27
src/views/InternalServerErrorView.vue
Normal file
@ -0,0 +1,27 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
const version = ref(__APP_VERSION__)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main>500 | Internal Server Error</main>
|
||||
<span>Version: {{ version }}</span>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
main {
|
||||
margin-top: -60px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: #39455f;
|
||||
font-family: 'roboto';
|
||||
font-size: 30px;
|
||||
}
|
||||
span {
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
143
src/views/MessageDocsView.vue
Normal file
143
src/views/MessageDocsView.vue
Normal file
@ -0,0 +1,143 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref, onBeforeMount, onMounted, inject, watch } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import SearchNav from '../components/MessageDocsSearchNav.vue'
|
||||
import { connectors } from '../obp/message-docs'
|
||||
import { obpGroupedMessageDocsKey } from '@/obp/keys';
|
||||
|
||||
let connector = connectors[0]
|
||||
const route = useRoute()
|
||||
const groupedMessageDocs = ref(inject(obpGroupedMessageDocsKey)!)
|
||||
const messageDocs = ref({})
|
||||
|
||||
onBeforeMount(() => {
|
||||
setDoc()
|
||||
})
|
||||
|
||||
watch(
|
||||
() => route.params.id,
|
||||
async (id) => {
|
||||
setDoc()
|
||||
}
|
||||
)
|
||||
|
||||
const setDoc = () => {
|
||||
const paramConnector = route.params.id
|
||||
if (connectors.includes(paramConnector)) {
|
||||
connector = paramConnector
|
||||
}
|
||||
messageDocs.value = groupedMessageDocs.value[connector]
|
||||
}
|
||||
function showRequiredFieldInfo(value: any) {
|
||||
return (JSON.stringify(value, null) === '{}' || JSON.stringify(value, null) === '')
|
||||
}
|
||||
function showDependentEndpoints(value: any) {
|
||||
return value.lengtt > 0
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-container>
|
||||
<el-aside class="search-nav" width="20%">
|
||||
<SearchNav />
|
||||
</el-aside>
|
||||
<el-main>
|
||||
<el-container class="main">
|
||||
<el-container>
|
||||
<main>
|
||||
<el-backtop :right="100" :bottom="100" target="main" />
|
||||
<div v-for="(group, key) of messageDocs" :key="key">
|
||||
<div v-for="(value, key) of group" :key="value">
|
||||
<el-divider content-position="left">{{ value.process }}</el-divider>
|
||||
<a v-bind:href="`#${value.process}`" :id="value.process">
|
||||
<h2>{{ value.description }}</h2>
|
||||
</a>
|
||||
<el-descriptions direction="vertical" :column="1" border>
|
||||
<el-descriptions-item label="Outbound Topic">
|
||||
{{ value.outbound_topic }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="Inbound Topic">
|
||||
{{ value.inbound_topic }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="Outbound Message">
|
||||
<pre>{{ JSON.stringify(value.example_outbound_message, null, 4) }}</pre>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="Inbound Message">
|
||||
<pre>{{ JSON.stringify(value.example_inbound_message, null, 4) }}</pre>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item v-if="showRequiredFieldInfo(value.requiredFieldInfo)" label="Required Fields">
|
||||
<pre>{{ JSON.stringify(value.requiredFieldInfo, null, 4) }}</pre>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item v-if="showDependentEndpoints(value.dependent_endpoints)"
|
||||
label="Dependent Endpoints">
|
||||
<ul>
|
||||
<li v-for="(endpoint, key) of value.dependent_endpoints">
|
||||
{{ endpoint.version }}: {{ endpoint.name }}
|
||||
</li>
|
||||
</ul>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<el-divider content-position="right">{{ value.process }}</el-divider>
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</el-container>
|
||||
</el-container>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.main {
|
||||
max-height: 90vh;
|
||||
}
|
||||
|
||||
template {
|
||||
overflow: auto;
|
||||
max-height: 900px;
|
||||
}
|
||||
|
||||
main {
|
||||
margin: 25px;
|
||||
color: #39455f;
|
||||
font-family: 'Roboto';
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
div {
|
||||
font-size: 14px;
|
||||
}
|
||||
pre {
|
||||
font-family: 'Roboto';
|
||||
}
|
||||
|
||||
.content :deep(strong) {
|
||||
font-family: 'Roboto';
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #39455f;
|
||||
}
|
||||
|
||||
.content :deep(a) {
|
||||
text-decoration: none;
|
||||
color: #ffffff;
|
||||
font-family: 'Roboto';
|
||||
font-size: 14px;
|
||||
border-radius: 3px;
|
||||
background-color: #52b165;
|
||||
padding: 1px;
|
||||
}
|
||||
|
||||
.content :deep(a):hover {
|
||||
background-color: #39455f;
|
||||
}
|
||||
</style>
|
||||
@ -34,6 +34,7 @@ export default defineConfig({
|
||||
__VUE_I18N_FULL_INSTALL__: true,
|
||||
__VUE_I18N_LEGACY_API__: false,
|
||||
__INTLIFY_PROD_DEVTOOLS__: false,
|
||||
__APP_VERSION__: JSON.stringify(process.env.npm_package_version)
|
||||
},
|
||||
server:{
|
||||
proxy: {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user