Enhance FaceAI integration with live checks and metadata handling
All checks were successful
Publish FaceAI Container / publish (push) Successful in 3m22s
All checks were successful
Publish FaceAI Container / publish (push) Successful in 3m22s
- Added optional live FaceAI checks in README.md - Implemented relative storage segment parsing in race-storage.js - Updated server.js to include relative directory in race storage - Refactored legacyAssets.js to resolve asset base URL dynamically - Expanded live race tests to validate FaceAI app launch and metadata - Introduced portrait image handling for live upload flow - Updated faceai_handoff.php to process race storage relative directory
This commit is contained in:
parent
c88b373c73
commit
bb60201ad4
9 changed files with 214 additions and 25 deletions
|
|
@ -201,6 +201,21 @@ What it does:
|
||||||
- opens the configured live race URL
|
- opens the configured live race URL
|
||||||
- verifies the account UI is present and the race search form renders correctly
|
- verifies the account UI is present and the race search form renders correctly
|
||||||
|
|
||||||
|
Optional live FaceAI checks can also be enabled with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
LIVE_FACEAI_BASE_URL=https://ai.regalamiunsorriso.it
|
||||||
|
LIVE_SITE_PORTRAIT_PATH=../test_pkl/live/test_portrait_1.png
|
||||||
|
LIVE_SITE_RUN_UPLOAD_FLOW=1
|
||||||
|
```
|
||||||
|
|
||||||
|
When enabled, the live suite also:
|
||||||
|
|
||||||
|
- validates that the legacy Face ID handoff URL includes the race storage metadata expected by FaceAI
|
||||||
|
- opens the real FaceAI app and asserts that the legacy header stylesheets load from the live legacy site
|
||||||
|
- confirms the app does not emit the `MISSING_RACE_STORAGE` invalid-race error on launch
|
||||||
|
- uploads the supplied portrait image and verifies that search creation succeeds
|
||||||
|
|
||||||
## Optional Backend And Frontend Dev Loop
|
## Optional Backend And Frontend Dev Loop
|
||||||
|
|
||||||
If you only want to iterate on the app without the PHP simulator, you can still run the public site and the processor separately. The queue-backed flow now requires Redis and the processor, so `npm run dev` alone is no longer the full stack.
|
If you only want to iterate on the app without the PHP simulator, you can still run the public site and the processor separately. The queue-backed flow now requires Redis and the processor, so `npm run dev` alone is no longer the full stack.
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,20 @@ function sanitizePathSegment(value) {
|
||||||
return normalized;
|
return normalized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parseRelativeStorageSegments(value) {
|
||||||
|
const normalized = String(value || '').trim().replace(/\\/g, '/');
|
||||||
|
|
||||||
|
if (!normalized) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalized
|
||||||
|
.split('/')
|
||||||
|
.map((segment) => segment.trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
.map((segment) => sanitizePathSegment(segment));
|
||||||
|
}
|
||||||
|
|
||||||
export function normalizeRaceFolderName(value) {
|
export function normalizeRaceFolderName(value) {
|
||||||
return String(value || '')
|
return String(value || '')
|
||||||
.trim()
|
.trim()
|
||||||
|
|
@ -49,9 +63,16 @@ export function buildMonthFolder(year, monthIndex) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildRaceStorage(storageInput = {}) {
|
export function buildRaceStorage(storageInput = {}) {
|
||||||
const year = sanitizePathSegment(storageInput.year);
|
const relativeSegments = parseRelativeStorageSegments(storageInput.relativeDir);
|
||||||
const monthFolder = sanitizePathSegment(storageInput.monthFolder);
|
const relativeYear = relativeSegments[0] || '';
|
||||||
const raceFolder = sanitizePathSegment(normalizeRaceFolderName(storageInput.raceFolder));
|
const relativeMonthFolder = relativeSegments.length >= 3 ? relativeSegments[1] : '';
|
||||||
|
const relativeRaceFolder = relativeSegments.length >= 3
|
||||||
|
? relativeSegments[2]
|
||||||
|
: (relativeSegments.length >= 2 ? relativeSegments[1] : '');
|
||||||
|
|
||||||
|
const year = sanitizePathSegment(storageInput.year || relativeYear);
|
||||||
|
const monthFolder = sanitizePathSegment(storageInput.monthFolder || relativeMonthFolder);
|
||||||
|
const raceFolder = sanitizePathSegment(normalizeRaceFolderName(storageInput.raceFolder || relativeRaceFolder));
|
||||||
|
|
||||||
if (!year || !monthFolder || !raceFolder) {
|
if (!year || !monthFolder || !raceFolder) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
||||||
|
|
@ -262,7 +262,8 @@ app.get('/dev/legacy/launch', (req, res) => {
|
||||||
raceStorage: {
|
raceStorage: {
|
||||||
year: String(req.query.raceYear || mockCatalog[raceId]?.storage?.year || ''),
|
year: String(req.query.raceYear || mockCatalog[raceId]?.storage?.year || ''),
|
||||||
monthFolder: String(req.query.raceMonthFolder || mockCatalog[raceId]?.storage?.monthFolder || ''),
|
monthFolder: String(req.query.raceMonthFolder || mockCatalog[raceId]?.storage?.monthFolder || ''),
|
||||||
raceFolder: String(req.query.raceFolder || mockCatalog[raceId]?.storage?.raceFolder || '')
|
raceFolder: String(req.query.raceFolder || mockCatalog[raceId]?.storage?.raceFolder || ''),
|
||||||
|
relativeDir: String(req.query.raceStorageRelativeDir || '')
|
||||||
},
|
},
|
||||||
lang,
|
lang,
|
||||||
returnUrl
|
returnUrl
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,33 @@
|
||||||
const legacyAssetBaseUrl = (import.meta.env.VITE_LEGACY_ASSET_BASE_URL || '/legacy-static').replace(/\/$/, '');
|
import { getLegacyBaseUrl } from './legacyUrls.js';
|
||||||
|
|
||||||
|
const localHostnames = new Set(['localhost', '127.0.0.1', '::1']);
|
||||||
|
|
||||||
|
function trimTrailingSlash(value) {
|
||||||
|
return String(value || '').replace(/\/$/, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function currentHostname() {
|
||||||
|
if (typeof window === 'undefined' || !window.location || !window.location.hostname) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return window.location.hostname.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveLegacyAssetBaseUrl() {
|
||||||
|
const configuredAssetBaseUrl = trimTrailingSlash(import.meta.env.VITE_LEGACY_ASSET_BASE_URL || '');
|
||||||
|
if (configuredAssetBaseUrl) {
|
||||||
|
return configuredAssetBaseUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (localHostnames.has(currentHostname())) {
|
||||||
|
return '/legacy-static';
|
||||||
|
}
|
||||||
|
|
||||||
|
return getLegacyBaseUrl();
|
||||||
|
}
|
||||||
|
|
||||||
|
const legacyAssetBaseUrl = resolveLegacyAssetBaseUrl();
|
||||||
|
|
||||||
export function legacyAsset(path) {
|
export function legacyAsset(path) {
|
||||||
return `${legacyAssetBaseUrl}${path.startsWith('/') ? path : `/${path}`}`;
|
return `${legacyAssetBaseUrl}${path.startsWith('/') ? path : `/${path}`}`;
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,59 @@
|
||||||
const { test, expect } = require('@playwright/test');
|
const { test, expect } = require('@playwright/test');
|
||||||
const {
|
const {
|
||||||
|
LIVE_FACEAI_BASE_URL,
|
||||||
|
LIVE_SITE_BASE_URL,
|
||||||
|
LIVE_SITE_PORTRAIT_PATH,
|
||||||
LIVE_SITE_RACE_URL,
|
LIVE_SITE_RACE_URL,
|
||||||
dismissCookieBanner,
|
LIVE_SITE_RUN_UPLOAD_FLOW,
|
||||||
expectRacePageLoaded,
|
ensureLiveAuthenticatedRacePage,
|
||||||
performLiveLogin,
|
requirePortraitFixture
|
||||||
waitForLoggedInUi
|
|
||||||
} = require('./live-site-test-utils');
|
} = require('./live-site-test-utils');
|
||||||
|
|
||||||
test('loads a live race page with an authenticated session', async ({ page }) => {
|
function escapeRegExp(value) {
|
||||||
await page.goto(LIVE_SITE_RACE_URL, { waitUntil: 'domcontentloaded' });
|
return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||||
await dismissCookieBanner(page);
|
}
|
||||||
|
|
||||||
try {
|
async function openLiveFaceAi(page) {
|
||||||
await waitForLoggedInUi(page);
|
const consoleErrors = [];
|
||||||
} catch (error) {
|
|
||||||
await performLiveLogin(page);
|
|
||||||
await page.goto(LIVE_SITE_RACE_URL, { waitUntil: 'domcontentloaded' });
|
|
||||||
await dismissCookieBanner(page);
|
|
||||||
await waitForLoggedInUi(page);
|
|
||||||
}
|
|
||||||
|
|
||||||
await expectRacePageLoaded(page);
|
page.on('console', (message) => {
|
||||||
|
if (message.type() === 'error') {
|
||||||
|
consoleErrors.push(message.text());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await ensureLiveAuthenticatedRacePage(page);
|
||||||
await expect(page.locator('h1')).toContainText(/HALF MARATHON FIRENZE|Competitions|Gare/i);
|
await expect(page.locator('h1')).toContainText(/HALF MARATHON FIRENZE|Competitions|Gare/i);
|
||||||
|
|
||||||
|
const launchUrl = await page.evaluate(() => {
|
||||||
|
return typeof buildFaceAiLaunchUrl === 'function' ? buildFaceAiLaunchUrl() : '';
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(launchUrl, 'Expected the legacy race page script to expose a FaceAI handoff URL builder.').toBeTruthy();
|
||||||
|
|
||||||
|
const parsedLaunchUrl = new URL(launchUrl, LIVE_SITE_BASE_URL);
|
||||||
|
expect(parsedLaunchUrl.searchParams.get('raceYear')).toBeTruthy();
|
||||||
|
expect(parsedLaunchUrl.searchParams.get('raceMonthFolder')).toBeTruthy();
|
||||||
|
expect(parsedLaunchUrl.searchParams.get('raceFolder')).toBeTruthy();
|
||||||
|
expect(parsedLaunchUrl.searchParams.get('raceStorageRelativeDir')).toBeTruthy();
|
||||||
|
|
||||||
|
await expect(page.locator('#faceaiLaunchButton')).toBeVisible();
|
||||||
|
await page.locator('#faceaiLaunchButton').click();
|
||||||
|
|
||||||
|
await page.waitForURL(new RegExp(`^${escapeRegExp(LIVE_FACEAI_BASE_URL)}/`), {
|
||||||
|
timeout: 60 * 1000
|
||||||
|
});
|
||||||
|
await expect(page.getByRole('heading', { name: /Trova le tue foto con un selfie|Find your photos with a selfie/i })).toBeVisible();
|
||||||
|
|
||||||
|
return {
|
||||||
|
consoleErrors,
|
||||||
|
launchUrl: parsedLaunchUrl
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
test('loads a live race page with an authenticated session', async ({ page }) => {
|
||||||
|
await ensureLiveAuthenticatedRacePage(page);
|
||||||
|
|
||||||
const cookies = await page.context().cookies(LIVE_SITE_RACE_URL);
|
const cookies = await page.context().cookies(LIVE_SITE_RACE_URL);
|
||||||
const faceAiIdentityCookie = cookies.find((cookie) => cookie.name === 'rus_faceai_identity');
|
const faceAiIdentityCookie = cookies.find((cookie) => cookie.name === 'rus_faceai_identity');
|
||||||
|
|
||||||
|
|
@ -30,4 +61,61 @@ test('loads a live race page with an authenticated session', async ({ page }) =>
|
||||||
expect(faceAiIdentityCookie.httpOnly).toBe(true);
|
expect(faceAiIdentityCookie.httpOnly).toBe(true);
|
||||||
expect(faceAiIdentityCookie.secure).toBe(true);
|
expect(faceAiIdentityCookie.secure).toBe(true);
|
||||||
expect(faceAiIdentityCookie.value).toMatch(/\./);
|
expect(faceAiIdentityCookie.value).toMatch(/\./);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('launches the live FaceAI app with race storage metadata and a styled header', async ({ page }) => {
|
||||||
|
const { consoleErrors, launchUrl } = await openLiveFaceAi(page);
|
||||||
|
|
||||||
|
expect(launchUrl.searchParams.get('raceStorageRelativeDir')).toContain('/');
|
||||||
|
await expect(page.locator('nav.navbar')).toBeVisible();
|
||||||
|
await expect(page.locator('link[data-legacy-href*="bootstrap.min.css"]')).toHaveCount(1);
|
||||||
|
await expect(page.locator('link[data-legacy-href*="custom-style.css"]')).toHaveCount(1);
|
||||||
|
|
||||||
|
const legacyStylesheetHrefs = await page.locator('link[data-legacy-href]').evaluateAll((elements) => {
|
||||||
|
return elements.map((element) => element.getAttribute('href') || '');
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(legacyStylesheetHrefs.some((href) => href.startsWith(`${LIVE_SITE_BASE_URL}/`))).toBe(true);
|
||||||
|
|
||||||
|
const navComputedStyles = await page.locator('nav.navbar').evaluate((element) => {
|
||||||
|
const styles = window.getComputedStyle(element);
|
||||||
|
return {
|
||||||
|
position: styles.position,
|
||||||
|
display: styles.display
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(navComputedStyles.position).toBe('fixed');
|
||||||
|
expect(navComputedStyles.display).not.toBe('block');
|
||||||
|
expect(consoleErrors.find((entry) => entry.includes('MISSING_RACE_STORAGE')) || '').toBe('');
|
||||||
|
expect(consoleErrors.find((entry) => entry.includes('[FaceAI] Invalid race data:')) || '').toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
test.skip(!LIVE_SITE_RUN_UPLOAD_FLOW, 'Set LIVE_SITE_RUN_UPLOAD_FLOW=1 to exercise the live upload flow.');
|
||||||
|
|
||||||
|
test('accepts the supplied portrait image for the live upload flow', async ({ page }) => {
|
||||||
|
requirePortraitFixture();
|
||||||
|
|
||||||
|
await openLiveFaceAi(page);
|
||||||
|
|
||||||
|
const fileInput = page.locator('input[type="file"]');
|
||||||
|
await expect(fileInput).toBeEnabled();
|
||||||
|
await fileInput.setInputFiles(LIVE_SITE_PORTRAIT_PATH);
|
||||||
|
|
||||||
|
await expect(page.locator('.faceai-file-name')).toContainText('test_portrait_1.png');
|
||||||
|
|
||||||
|
const searchResponsePromise = page.waitForResponse((response) => {
|
||||||
|
return response.request().method() === 'POST' && response.url().includes('/api/searches');
|
||||||
|
}, {
|
||||||
|
timeout: 60 * 1000
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.getByRole('button', { name: /Avvia ricerca Face ID|Start Face ID search/i }).click();
|
||||||
|
|
||||||
|
const searchResponse = await searchResponsePromise;
|
||||||
|
expect(searchResponse.ok(), 'Expected the live upload flow to create a FaceAI search successfully.').toBe(true);
|
||||||
|
|
||||||
|
const searchPayload = await searchResponse.json();
|
||||||
|
expect(searchPayload.id || searchPayload.searchId, 'Expected the search creation response to include a search identifier.').toBeTruthy();
|
||||||
|
await expect(page.locator('.faceai-feedback')).not.toContainText(/Impossibile|Unable|Errore|Error/i);
|
||||||
});
|
});
|
||||||
|
|
@ -2,11 +2,15 @@ const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const { expect } = require('@playwright/test');
|
const { expect } = require('@playwright/test');
|
||||||
|
|
||||||
|
const WORKSPACE_ROOT = path.resolve(__dirname, '..', '..', '..');
|
||||||
const LIVE_SITE_BASE_URL = process.env.LIVE_SITE_BASE_URL || 'https://www.regalamiunsorriso.it';
|
const LIVE_SITE_BASE_URL = process.env.LIVE_SITE_BASE_URL || 'https://www.regalamiunsorriso.it';
|
||||||
const LIVE_SITE_LOGIN_URL = process.env.LIVE_SITE_LOGIN_URL || `${LIVE_SITE_BASE_URL}/login_clienti-it.html`;
|
const LIVE_SITE_LOGIN_URL = process.env.LIVE_SITE_LOGIN_URL || `${LIVE_SITE_BASE_URL}/login_clienti-it.html`;
|
||||||
const LIVE_SITE_RACE_URL = process.env.LIVE_SITE_RACE_URL || `${LIVE_SITE_BASE_URL}/42%20HALF%20MARATHON%20FIRENZE_gara-1018545---96-1.html`;
|
const LIVE_SITE_RACE_URL = process.env.LIVE_SITE_RACE_URL || `${LIVE_SITE_BASE_URL}/42%20HALF%20MARATHON%20FIRENZE_gara-1018545---96-1.html`;
|
||||||
|
const LIVE_FACEAI_BASE_URL = process.env.LIVE_FACEAI_BASE_URL || 'https://ai.regalamiunsorriso.it';
|
||||||
const LIVE_SITE_USERNAME = process.env.LIVE_SITE_USERNAME || '';
|
const LIVE_SITE_USERNAME = process.env.LIVE_SITE_USERNAME || '';
|
||||||
const LIVE_SITE_PASSWORD = process.env.LIVE_SITE_PASSWORD || '';
|
const LIVE_SITE_PASSWORD = process.env.LIVE_SITE_PASSWORD || '';
|
||||||
|
const LIVE_SITE_PORTRAIT_PATH = process.env.LIVE_SITE_PORTRAIT_PATH || path.join(WORKSPACE_ROOT, 'test_pkl', 'live', 'test_portrait_1.png');
|
||||||
|
const LIVE_SITE_RUN_UPLOAD_FLOW = process.env.LIVE_SITE_RUN_UPLOAD_FLOW === '1';
|
||||||
const AUTH_FILE = path.join(__dirname, '.auth', 'user.json');
|
const AUTH_FILE = path.join(__dirname, '.auth', 'user.json');
|
||||||
|
|
||||||
function ensureAuthDirectory() {
|
function ensureAuthDirectory() {
|
||||||
|
|
@ -75,18 +79,45 @@ async function expectRacePageLoaded(page) {
|
||||||
await expect(page.locator('script[src*="_js/rus-ecom-240621.js"]')).toHaveCount(1);
|
await expect(page.locator('script[src*="_js/rus-ecom-240621.js"]')).toHaveCount(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function ensureLiveAuthenticatedRacePage(page) {
|
||||||
|
await page.goto(LIVE_SITE_RACE_URL, { waitUntil: 'domcontentloaded' });
|
||||||
|
await dismissCookieBanner(page);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await waitForLoggedInUi(page);
|
||||||
|
} catch (error) {
|
||||||
|
await performLiveLogin(page);
|
||||||
|
await page.goto(LIVE_SITE_RACE_URL, { waitUntil: 'domcontentloaded' });
|
||||||
|
await dismissCookieBanner(page);
|
||||||
|
await waitForLoggedInUi(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
await expectRacePageLoaded(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
function requirePortraitFixture() {
|
||||||
|
if (!fs.existsSync(LIVE_SITE_PORTRAIT_PATH)) {
|
||||||
|
throw new Error(`LIVE_SITE_PORTRAIT_PATH does not exist: ${LIVE_SITE_PORTRAIT_PATH}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
AUTH_FILE,
|
AUTH_FILE,
|
||||||
|
LIVE_FACEAI_BASE_URL,
|
||||||
LIVE_SITE_BASE_URL,
|
LIVE_SITE_BASE_URL,
|
||||||
LIVE_SITE_LOGIN_URL,
|
LIVE_SITE_LOGIN_URL,
|
||||||
LIVE_SITE_PASSWORD,
|
LIVE_SITE_PASSWORD,
|
||||||
|
LIVE_SITE_PORTRAIT_PATH,
|
||||||
LIVE_SITE_RACE_URL,
|
LIVE_SITE_RACE_URL,
|
||||||
|
LIVE_SITE_RUN_UPLOAD_FLOW,
|
||||||
LIVE_SITE_USERNAME,
|
LIVE_SITE_USERNAME,
|
||||||
dismissCookieBanner,
|
dismissCookieBanner,
|
||||||
|
ensureLiveAuthenticatedRacePage,
|
||||||
ensureAuthDirectory,
|
ensureAuthDirectory,
|
||||||
expectRacePageLoaded,
|
expectRacePageLoaded,
|
||||||
loginSubmitLocator,
|
loginSubmitLocator,
|
||||||
performLiveLogin,
|
performLiveLogin,
|
||||||
|
requirePortraitFixture,
|
||||||
requireCredentials,
|
requireCredentials,
|
||||||
waitForLoggedInUi
|
waitForLoggedInUi
|
||||||
};
|
};
|
||||||
BIN
test_pkl/live/test_portrait_1.png
Normal file
BIN
test_pkl/live/test_portrait_1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 166 KiB |
|
|
@ -258,6 +258,7 @@ function buildFaceAiLaunchUrl() {
|
||||||
var raceYear = getFaceAiStorageValue("faceAiRaceYear", "year");
|
var raceYear = getFaceAiStorageValue("faceAiRaceYear", "year");
|
||||||
var raceMonthFolder = getFaceAiStorageValue("faceAiRaceMonthFolder", "monthFolder");
|
var raceMonthFolder = getFaceAiStorageValue("faceAiRaceMonthFolder", "monthFolder");
|
||||||
var raceFolder = getFaceAiStorageValue("faceAiRaceFolder", "raceFolder");
|
var raceFolder = getFaceAiStorageValue("faceAiRaceFolder", "raceFolder");
|
||||||
|
var raceStorageRelativeDir = $("#faceAiRaceStorageRelativeDir").val() || [raceYear, raceMonthFolder, raceFolder].filter(Boolean).join("/");
|
||||||
var lang = getCurrentLangValue();
|
var lang = getCurrentLangValue();
|
||||||
var handoffUrl = (window.faceAiSimulator && window.faceAiSimulator.handoffUrl) || "faceai_handoff.php";
|
var handoffUrl = (window.faceAiSimulator && window.faceAiSimulator.handoffUrl) || "faceai_handoff.php";
|
||||||
var returnUrl = (window.faceAiSimulator && window.faceAiSimulator.returnUrl) || window.location.href;
|
var returnUrl = (window.faceAiSimulator && window.faceAiSimulator.returnUrl) || window.location.href;
|
||||||
|
|
@ -268,6 +269,7 @@ function buildFaceAiLaunchUrl() {
|
||||||
"raceYear=" + encodeURIComponent(raceYear),
|
"raceYear=" + encodeURIComponent(raceYear),
|
||||||
"raceMonthFolder=" + encodeURIComponent(raceMonthFolder),
|
"raceMonthFolder=" + encodeURIComponent(raceMonthFolder),
|
||||||
"raceFolder=" + encodeURIComponent(raceFolder),
|
"raceFolder=" + encodeURIComponent(raceFolder),
|
||||||
|
"raceStorageRelativeDir=" + encodeURIComponent(raceStorageRelativeDir),
|
||||||
"lang=" + encodeURIComponent(lang),
|
"lang=" + encodeURIComponent(lang),
|
||||||
"returnUrl=" + encodeURIComponent(returnUrl)
|
"returnUrl=" + encodeURIComponent(returnUrl)
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ try {
|
||||||
$raceYear = faceai_request_value('raceYear');
|
$raceYear = faceai_request_value('raceYear');
|
||||||
$raceMonthFolder = faceai_request_value('raceMonthFolder');
|
$raceMonthFolder = faceai_request_value('raceMonthFolder');
|
||||||
$raceFolder = faceai_request_value('raceFolder');
|
$raceFolder = faceai_request_value('raceFolder');
|
||||||
|
$raceStorageRelativeDir = faceai_request_value('raceStorageRelativeDir');
|
||||||
$lang = faceai_request_value('lang', 'it');
|
$lang = faceai_request_value('lang', 'it');
|
||||||
$returnUrl = faceai_request_value('returnUrl');
|
$returnUrl = faceai_request_value('returnUrl');
|
||||||
|
|
||||||
|
|
@ -45,11 +46,12 @@ try {
|
||||||
'name' => $raceName !== '' ? $raceName : $raceId
|
'name' => $raceName !== '' ? $raceName : $raceId
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($raceYear !== '' && $raceMonthFolder !== '' && $raceFolder !== '') {
|
if ($raceYear !== '' || $raceMonthFolder !== '' || $raceFolder !== '' || $raceStorageRelativeDir !== '') {
|
||||||
$racePayload['storage'] = array(
|
$racePayload['storage'] = array(
|
||||||
'year' => $raceYear,
|
'year' => $raceYear,
|
||||||
'monthFolder' => $raceMonthFolder,
|
'monthFolder' => $raceMonthFolder,
|
||||||
'raceFolder' => strtoupper(trim($raceFolder))
|
'raceFolder' => strtoupper(trim($raceFolder)),
|
||||||
|
'relativeDir' => $raceStorageRelativeDir
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue