「表EBA_COUNTRIESにはどのようなデータが含まれていますか?」
日日是Oracle APEX
Oracle APEXを使った作業をしていて、気の付いたところを忘れないようにメモをとります。
2025年4月7日月曜日
VS CodeのGitHub Copilot Agent modeにOracleに問い合わせるMCPサーバーを組み込む
「表EBA_COUNTRIESにはどのようなデータが含まれていますか?」
2025年4月1日火曜日
ChainlitのアプリケーションにOracle Databaseに問い合わせるMCPサーバーを組み込み質問する
「Oracle Databaseから参照できる表を一覧してください。」
「EBAで始まる表の列情報を参照してください。」
ビューUSER_TAB_COLUMNSを検索して、それぞれの表の列を一覧します。
「これらの情報より、アジアで一番人口の少ない国を調べてください。」
SELECT c.NAME, c.POPULATION FROM EBA_COUNTRIES c JOIN EBA_COUNTRY_REGIONS r ON c.REGION_ID = r.ID WHERE r.NAME = 'Asia' ORDER BY c.POPULATION ASC FETCH FIRST 1 ROW ONLY;
SELECT c.NAME, c.POPULATION FROM EBA_COUNTRIES c JOIN EBA_COUNTRY_REGIONS r ON c.REGION_ID = r.ID WHERE r.NAME = 'Asia' ORDER BY c.POPULATION DESC;
ChainlitのアプリケーションよりPlaywright MCPを呼び出してOracle APEXのアプリケーションを操作する
Azure OpenAIでMCPを使ったデモアプリを試す
https://zenn.dev/headwaters/articles/223f212b4c1cd6
Azure OpenAI MCP Integration
https://github.com/monuminu/AOAI_Samples/tree/main/mcp_aoai
% mkdir mcp_aoai
% cd mcp_aoai
mcp_aoai %
python3 -m venv mcp_aoai
source mcp_aoai/bin/activate
mcp_aoai % python3 -V
Python 3.13.2
mcp_aoai % python3 -m venv mcp_aoai
mcp_aoai % source mcp_aoai/bin/activate
(mcp_aoai) mcp_aoai %
pip install chainlit openai python-dotenv aiohttp
(mcp_aoai) mcp_aoai % pip install chainlit openai python-dotenv aiohttp
Collecting chainlit
Using cached chainlit-2.4.400-py3-none-any.whl.metadata (6.3 kB)
Collecting openai
Using cached openai-1.70.0-py3-none-any.whl.metadata (25 kB)
Collecting python-dotenv
Using cached python_dotenv-1.1.0-py3-none-any.whl.metadata (24 kB)
Collecting aiohttp
Using cached aiohttp-3.11.12-cp313-cp313-macosx_11_0_arm64.whl.metadata (7.7 kB)
Collecting aiofiles<25.0.0,>=23.1.0 (from chainlit)
Using cached aiofiles-24.1.0-py3-none-any.whl.metadata (10 kB)
Collecting asyncer<0.0.8,>=0.0.7 (from chainlit)
Using cached asyncer-0.0.7-py3-none-any.whl.metadata (6.6 kB)
Collecting click<9.0.0,>=8.1.3 (from chainlit)
[中略]
o-5.12.1 pyyaml-6.0.2 regex-2024.11.6 requests-2.32.3 simple-websocket-1.1.0 six-1.17.0 sniffio-1.3.1 sse-starlette-2.2.1 starlette-0.41.3 syncer-2.0.3 tenacity-8.5.0 tiktoken-0.9.0 tokenizers-0.21.1 tomli-2.2.1 tqdm-4.67.1 traceloop-sdk-0.39.0 typing-extensions-4.13.0 typing-inspect-0.9.0 typing-inspection-0.4.0 uptrace-1.31.0 urllib3-2.3.0 uvicorn-0.34.0 watchfiles-0.20.0 wrapt-1.17.2 wsproto-1.2.0 yarl-1.18.3 zipp-3.21.0
[notice] A new release of pip is available: 25.0 -> 25.0.1
[notice] To update, run: pip install --upgrade pip
(mcp_aoai) mcp_aoai %
8c8
< from openai import AzureOpenAI, AsyncAzureOpenAI
---
> from openai import OpenAI, AsyncOpenAI
17,21c17,19
< self.deployment_name = os.environ["AZURE_OPENAI_MODEL"]
< self.client = AsyncAzureOpenAI(
< azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
< api_key=os.environ["AZURE_OPENAI_API_KEY"],
< api_version=os.environ["OPENAI_API_VERSION"]
---
> self.deployment_name = os.environ["OPENAI_MODEL"]
> self.client = AsyncOpenAI(
> api_key=os.environ["OPENAI_API_KEY"]
export OPENAI_MODEL=gpt-4o-mini-2024-07-18
export OPENAI_API_KEY=OpenAIのAPIキー
(mcp_aoai) mcp_aoai % export OPENAI_MODEL=gpt-4o-mini-2024-07-18
(mcp_aoai) mcp_aoai % export OPENAI_API_KEY=OpenAIのAPIキー
(mcp_aoai) mcp_aoai %
以上で準備は完了です。
(mcp_aoai) mcp_aoai % chainlit run app.py
2025-04-01 11:12:09 - Your app is available at http://localhost:8000
2025-04-01 11:12:10 - Translated markdown file for ja not found. Defaulting to chainlit.md.
確認をクリックします。
2025年3月29日土曜日
Playwright MCPを使ってOracle APEXのアプリケーションを操作する
- http://localhost:8181/ords/r/apexdev/demonstration-emp-dept/dashboardを開いて
- ユーザー名とパスワードは自分で入力します。
- サインインできました。
- Employeesの一覧を開いて
- SCOTTのSalaryを3000から4000に変更して
- いえ、これでいいです。ありがとう!
MicrosoftのPlaywrightを使ってAPEXアプリケーションのE2Eテストを行う
- Dashboardのカードをクリックし、Dashboardのページが開くことを確認します。
- Employeesのカードをクリックし、Employeesのファセット検索のページが開くことを確認します。
- Departmentsのカードをクリックし、Departmentsの対話モード・レポートのページが開くことを確認します。
<input type="text" id="P9999_USERNAME" name="P9999_USERNAME" placeholder="ユーザー名" class="text_field apex-item-text apex-item-has-icon" autocomplete="username" value="admin" size="40" maxlength="100" data-enter-submit="false" data-is-page-item-type="true">
<button class="t-Button t-Button--hot lto18565948643221472_0" type="button" id="B18565948643221472" data-otel-label="LOGIN"><span class="t-Button-label">サインイン</span></button>
<button class="t-Button t-Button--hot lto18565948643221472_0" type="button" id="LOGIN" data-otel-label="LOGIN"><span class="t-Button-label">サインイン</span></button>
<a href="/ords/r/apexdev/demonstration-emp-dept106/dashboard?session=8435741618687" class="t-Card-wrap" data-otel-label="">
<div class="t-Card-icon u-color "><span class="t-Icon fa fa-dashboard"><span class="t-Card-initials" role="presentation"></span></span></div>
<div class="t-Card-titleWrap">
<h3 class="t-Card-title">Dashboard</h3>
<h4 class="t-Card-subtitle"></h4>
</div>
<div class="t-Card-body">
<div class="t-Card-desc"></div>
<div class="t-Card-info"></div>
</div>
<span class="t-Card-colorFill u-color " aria-hidden="true"></span>
</a>
ページの設定を確認します。<a href="#LINK#" class="t-Card-wrap" #A05# data-otel-label="#A10#">
% mkdir Playwright
%
% cd Playwright
Playwright % npm init playwright@latest
> npx
> create-playwright
Getting started with writing end-to-end tests with Playwright:
Initializing project in '.'
✔ Do you want to use TypeScript or JavaScript? · TypeScript
✔ Where to put your end-to-end tests? · tests
✔ Add a GitHub Actions workflow? (y/N) · false
✔ Install Playwright browsers (can be done manually via 'npx playwright install')? (Y/n) · true
Initializing NPM project (npm init -y)…
Wrote to /Users/ynakakoshi/Documents/Playwright/package.json:
{
"name": "playwright",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": ""
}
Installing Playwright Test (npm install --save-dev @playwright/test)…
added 3 packages, and audited 4 packages in 1s
found 0 vulnerabilities
Installing Types (npm install --save-dev @types/node)…
added 3 packages, and audited 7 packages in 640ms
found 0 vulnerabilities
Writing playwright.config.ts.
Writing tests/example.spec.ts.
Writing tests-examples/demo-todo-app.spec.ts.
Writing package.json.
Downloading browsers (npx playwright install)…
✔ Success! Created a Playwright Test project at /Users/ynakakoshi/Documents/Playwright
Inside that directory, you can run several commands:
npx playwright test
Runs the end-to-end tests.
npx playwright test --ui
Starts the interactive UI mode.
npx playwright test --project=chromium
Runs the tests only on Desktop Chrome.
npx playwright test example
Runs the tests in a specific file.
npx playwright test --debug
Runs the tests in debug mode.
npx playwright codegen
Auto generate tests with Codegen.
We suggest that you begin by typing:
npx playwright test
And check out the following files:
- ./tests/example.spec.ts - Example end-to-end test
- ./tests-examples/demo-todo-app.spec.ts - Demo Todo App end-to-end tests
- ./playwright.config.ts - Playwright Test configuration
Visit https://playwright.dev/docs/intro for more information. ✨
Happy hacking! 🎭
Playwright %
import { defineConfig, devices } from '@playwright/test'; | |
/** | |
* Read environment variables from file. | |
* https://github.com/motdotla/dotenv | |
*/ | |
// import dotenv from 'dotenv'; | |
// import path from 'path'; | |
// dotenv.config({ path: path.resolve(__dirname, '.env') }); | |
/** | |
* See https://playwright.dev/docs/test-configuration. | |
*/ | |
export default defineConfig({ | |
testDir: './tests', | |
/* Run tests in files in parallel */ | |
fullyParallel: true, | |
/* Fail the build on CI if you accidentally left test.only in the source code. */ | |
forbidOnly: !!process.env.CI, | |
/* Retry on CI only */ | |
retries: process.env.CI ? 2 : 0, | |
/* Opt out of parallel tests on CI. */ | |
workers: process.env.CI ? 1 : undefined, | |
/* Reporter to use. See https://playwright.dev/docs/test-reporters */ | |
reporter: 'html', | |
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ | |
use: { | |
/* Base URL to use in actions like `await page.goto('/')`. */ | |
// baseURL: 'http://127.0.0.1:3000', | |
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ | |
trace: 'on-first-retry', | |
}, | |
/* Configure projects for major browsers */ | |
projects: [ | |
{ | |
name: 'chromium', | |
use: { | |
...devices['Desktop Chrome'], | |
viewport: { width: 1280, height: 1280 }, | |
storageState: './.auth/state.json', | |
}, | |
}, | |
{ | |
name: 'firefox', | |
use: { ...devices['Desktop Firefox'] }, | |
}, | |
{ | |
name: 'webkit', | |
use: { ...devices['Desktop Safari'] }, | |
}, | |
/* Test against mobile viewports. */ | |
// { | |
// name: 'Mobile Chrome', | |
// use: { ...devices['Pixel 5'] }, | |
// }, | |
// { | |
// name: 'Mobile Safari', | |
// use: { ...devices['iPhone 12'] }, | |
// }, | |
/* Test against branded browsers. */ | |
// { | |
// name: 'Microsoft Edge', | |
// use: { ...devices['Desktop Edge'], channel: 'msedge' }, | |
// }, | |
// { | |
// name: 'Google Chrome', | |
// use: { ...devices['Desktop Chrome'], channel: 'chrome' }, | |
// }, | |
], | |
/* Run your local dev server before starting the tests */ | |
// webServer: { | |
// command: 'npm run start', | |
// url: 'http://127.0.0.1:3000', | |
// reuseExistingServer: !process.env.CI, | |
// }, | |
}); |
import { test as base } from "@playwright/test"; | |
import path from 'path'; | |
import * as fs from 'fs'; | |
/* | |
* プロジェクトのフォルダ直下に.authフォルダを作成し、その中にauth.jsonを作成する。 | |
* auth.jsonの中身は以下のようにする。 | |
* ログイン・ユーザー名とパスワードは、実際のものに置き換えること。 | |
* {"username":"admin","password":"4fae#ae2ef3!!","sessionId":"17291688537780"} | |
* | |
* 初回実行時のために、state.jsonファイルを作成しておく。 | |
* echo "{}" > .auth/state.json | |
* | |
* BASE_URLは、テスト実行時に使用するURLを指定する。 | |
*/ | |
export const authFile = path.join(__dirname, '../.auth/auth.json'); | |
export const BASE_URL = 'http://localhost:8181/ords/r/apexdev/demonstration-emp-dept'; | |
const stateFile = path.join(__dirname, '../.auth/state.json'); | |
export const test = base.extend({ | |
// Create a custom fixture | |
loginUser: async ({ page }, use) => { | |
// auth.jsonからユーザー名とパスワードを取り出す。 | |
const auth = JSON.parse(fs.readFileSync(authFile, 'utf8')); | |
/* | |
* 最初にホーム・ページにアクセスするが、URLにsession=が含まれていないため、ログイン・ページに遷移する。 | |
*/ | |
await page.goto(`${BASE_URL}/home`); | |
/* | |
* ページ・作成ウィザードが生成したログイン・ページであれば、 | |
* ページ・アイテム名がIDと同じINPUT要素が作成されている。 | |
*/ | |
await page.fill("input#P9999_USERNAME", auth.username); | |
await page.fill("input#P9999_PASSWORD", auth.password); | |
// await page.click("input#P9999_REMEMBER"); | |
// サインイン・ボタンをクリックする。ボタンに静的IDとしてLOGINを設定する必要がある。 | |
await page.click("button#LOGIN"); | |
/* | |
* サインインに成功した後、ホーム・ページに遷移する。 | |
* ページ・遷移後のURLからsessioin=の引数に設定されている | |
* セッションIDを取り出し、auth.jsonに保存する。 | |
* このセッションIDを後続のテストで使用する。 | |
* | |
* 完全にホーム・ページに遷移したことを確認するためにwaitForSelectorを呼び出しているが、 | |
* もっと汎用的な方法があるはず。 | |
*/ | |
await page.waitForSelector('#menu-dashboard'); | |
// URLからセッションIDを取り出す。 | |
const homeURL = new URL(page.url()); | |
const sessionId = homeURL.searchParams.get('session'); | |
// auth.jsonにsessionIdとして保存する。 | |
const newAuth = { ...auth, sessionId: sessionId }; | |
fs.writeFileSync(authFile, JSON.stringify(newAuth)); | |
// console.log('afer signin - auth', newAuth); | |
/* | |
* contextをstate.jsonに保存する。 | |
*/ | |
await page.context().storageState({ path: stateFile }); | |
// const cookies = await page.context().cookies(); | |
// console.log('cookies', cookies); | |
// Pass control back to the test | |
await use(); | |
}, | |
}); |
{"username":"admin","password":"4fae#ae2ef3!!","sessionId":"17291688537780"}
import { expect } from '@playwright/test'; | |
import { test, authFile, BASE_URL } from './fixtures.ts'; | |
import path from 'path'; | |
import * as fs from 'fs'; | |
test('open Dashboard', async ({ page, loginUser }) => { | |
/* | |
* fixtures.jsに記載されているloginUserが呼び出される。 | |
* サインインに成功した後は、ホーム・ページが開かれている。 | |
*/ | |
// 表示されているページにダッシュボードのカードがあることを確認する。 | |
await page.waitForSelector('#menu-dashboard', { timeout: 5000 }); | |
// ダッシュボードのカードをクリックする。#menu-dashboardはAPEX側に要設定。 | |
await page.click("a#menu-dashboard"); | |
// ダッシュボードのページが開かれていることを確認する。 | |
await expect(page).toHaveTitle('Dashboard'); | |
}); | |
/* | |
test('verify', async ({ page }) => { | |
// call page without fixture. | |
await page.goto('http://localhost:8181/ords/apexdev/test/test'); | |
}); | |
*/ | |
test('open Employees', async ({ page }) => { | |
/* | |
* loginUserは呼び出さず、サインインが継続している状態から始める。 | |
* auth.jsonに保存されているsessionIdを使用して、ホームページを開く。 | |
* auth.jsonは、fixtures.jsのloginUserで、サインイン時にsessionIdが更新されている。 | |
*/ | |
const auth = JSON.parse(fs.readFileSync(authFile, 'utf8')); | |
// ホーム・ページを開く。 | |
const home_url = `${BASE_URL}/home?session=${auth.sessionId}`; | |
console.log(home_url); | |
await page.goto(home_url); | |
// ホーム・ページに従業員一覧のカードがあることを確認し、クリックする。#menu-employeesはAPEX側に要設定。 | |
await page.waitForSelector('#menu-employees', { timeout: 3000 }); | |
await page.click("a#menu-employees"); | |
// 従業員のページが開かれていることを確認する。 | |
await expect(page).toHaveTitle('Employees'); | |
}); | |
test('open Departments', async ({ page }) => { | |
/* | |
* 従業員一覧に同じ。 | |
*/ | |
const auth = JSON.parse(fs.readFileSync(authFile, 'utf8')); | |
// ホーム・ページを開く。 | |
const home_url = `${BASE_URL}/home?session=${auth.sessionId}`; | |
console.log(home_url); | |
await page.goto(home_url); | |
// ホーム・ページに部門一覧のカードがあることを確認し、クリックする。#menu-departmentsはAPEX側に要設定。 | |
await page.waitForSelector('#menu-departments', { timeout: 3000 }); | |
await page.click("a#menu-departments"); | |
// 部門のページが開かれていることを確認する。 | |
await expect(page).toHaveTitle('Departments'); | |
}); |