Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 1 addition & 0 deletions src/app/lib/logger.const.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const logCodes = {
DATA_REQUEST_RECEIVED: 'data_request_received',
DATA_RESPONSE_FROM_CACHE: 'data_response_from_cache',
BFF_FETCH_ERROR: 'bff_fetch_error',
SPORT_DATA_FETCH_ERROR: 'sport_data_fetch_error',
IDCTA_FETCH_ERROR: 'idcta_fetch_error',

// Files
Expand Down
138 changes: 138 additions & 0 deletions src/app/routes/utils/fetchDataFromSportData/fixture.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
{
"title": "Villa gain upper hand with gritty Europa League win at Bologna",
"live": false,
"startDateTime": "2026-04-09T19:00:00.000Z",
"uasActivityData": {
"action": "heartbeat",
"resourceDomain": "bbc-live",
"resourceId": "urn:bbc:tipo:topic:ce847pmy2e7t",
"resourceType": "asset",
"environment": "live",
"apiKey": "km5ifdffcjdoc"
},
"countingServiceDataAverage": 0,
"sportDataEvent": {
"urn": "urn:bbc:sportsdata:football:event:s-3y91hnyfjh24yxjhm77a7hy50",
"home": {
"id": "ej5er0oyngdw138yuumwqbyqt",
"fullName": "Bologna",
"shortName": "Bologna",
"urn": "urn:bbc:sportsdata:football:team:bologna",
"runningScores": { "halftime": "0", "fulltime": "1", "aggregate": "1" },
"scoreUnconfirmed": "1",
"actions": [
{
"playerUrn": "urn:bbc:sportsdata:football:player:s-2bmeynv0dhsc8sjfuaprkexre",
"playerName": "J. Rowe",
"actionType": "goal",
"actions": [
{
"type": "Goal",
"typeLabel": { "value": "Goal", "accessible": "Goal" },
"timeLabel": { "value": "90'", "accessible": "90 minutes" }
}
]
}
],
"score": "1"
},
"away": {
"id": "b496gs285it6bheuikox6z9mj",
"fullName": "Aston Villa",
"shortName": "Aston Villa",
"urn": "urn:bbc:sportsdata:football:team:aston-villa",
"runningScores": { "halftime": "1", "fulltime": "3", "aggregate": "3" },
"scoreUnconfirmed": "3",
"actions": [
{
"playerUrn": "urn:bbc:sportsdata:football:player:s-8qys6qtdwgsycxducl062zld5",
"playerName": "E. Konsa",
"actionType": "goal",
"actions": [
{
"type": "Goal",
"typeLabel": { "value": "Goal", "accessible": "Goal" },
"timeLabel": { "value": "44'", "accessible": "44 minutes" }
}
]
},
{
"playerUrn": "urn:bbc:sportsdata:football:player:s-5m0j33eoa5c8pqlr0tdf7undh",
"playerName": "O. Watkins",
"actionType": "goal",
"actions": [
{
"type": "Goal",
"typeLabel": { "value": "Goal", "accessible": "Goal" },
"timeLabel": { "value": "51'", "accessible": "51 minutes" }
},
{
"type": "Goal",
"typeLabel": { "value": "Goal", "accessible": "Goal" },
"timeLabel": {
"value": "90'+4",
"accessible": "90 minutes plus 4"
}
}
]
}
],
"score": "3"
},
"time": {
"accessibleTime": "20:00",
"displayTimeUK": "20:00",
"timeCertainty": true
},
"date": "Thu 9 Apr 2026",
"tournament": {
"id": "4c1nfi2j1m731hcay25fcgndq",
"name": "UEFA Europa League",
"disambiguatedName": "UEFA Europa League",
"urn": "urn:bbc:sportsdata:football:tournament:europa-league",
"thingsGuid": "2afbdda7-71d4-544d-bcc6-d9ff50314b2a"
},
"stage": {
"id": "7wxuj38kqm8bz3cmi15vu4w7o",
"name": "Quarter-finals",
"urn": ""
},
"multiLeg": { "leg": 1, "relatedMatchId": "s-9ur6e6w5f4ahyxph7ef4rks2c" },
"period": "ft",
"venue": {
"id": "2nrn0y55nz9ee7p9adzbb7fta",
"urn": "urn:bbc:sportsdata:football:venue:s-2nrn0y55nz9ee7p9adzbb7fta",
"name": "Stadio Renato Dall'Ara",
"shortName": "Stadio Renato Dall'Ara"
},
"attendance": { "value": 31142 },
"status": "PostEvent",
"periodLabel": { "value": "FT", "accessible": "Full time" },
"winner": "away",
"tournamentDescriptionLabel": "UEFA Europa League - Quarter-finals",
"groupedActions": [
{
"groupName": { "fullName": "Assists", "shortName": "Assists" },
"homeTeamActions": ["J. Lucumí (90')"],
"awayTeamActions": ["Y. Tielemans (44', 90'+4)", "E. Buendía (51')"]
}
],
"accessibleEventSummary": "Bologna 1 , Aston Villa 3 at Full time",
"sportDiscipline": "football"
},
"staticLinksData": {
"staticLinks": [
{
"url": "/sport/football/live/clygly9z1j9t",
"text": "Relive Thursday's Europa League and Conference League action"
}
]
},
"type": "oppm",
"headerImage": null,
"seoTimestamps": {
"firstPublished": "2026-04-08T03:00:55.128Z",
"lastModified": "2026-04-10T08:37:45.000Z"
},
"lastUpdatedString": "Updated 6 days ago"
}
58 changes: 58 additions & 0 deletions src/app/routes/utils/fetchDataFromSportData/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import Url from 'url-parse';
import fetchPageData from '#app/routes/utils/fetchPageData';
import getErrorStatusCode from '#app/routes/utils/fetchPageData/utils/getErrorStatusCode';
import { SPORT_DATA_FETCH_ERROR } from '#lib/logger.const';
import { FetchError, GetAgent } from '#models/types/fetch';
import nodeLogger from '#lib/logger.node';

const logger = nodeLogger(__filename);

interface fetchDataFromSportData {
type: string;
urn: string;
getAgent?: GetAgent;
}

export default async ({ type, urn, getAgent }: fetchDataFromSportData) => {
const queryParameters = {
type,
urn,
};

const fetchUrl = Url('https://fabl.api.bbci.co.uk/module/sport-data').set(
'query',
queryParameters,
);

const useCerts = true;

const agent = useCerts && getAgent ? await getAgent() : undefined;
const timeout = useCerts ? undefined : 60000;

try {
const fetchPageDataArgs = {
path: fetchUrl.toString(),
...(agent && { agent }),
...(timeout && { timeout }),
};

// @ts-expect-error - Ignore fetchPageData argument types
const { status, json } = await fetchPageData(fetchPageDataArgs);

return {
status,
json,
};
} catch (error: unknown) {
const { message, status = getErrorStatusCode() } = error as FetchError;

logger.error(SPORT_DATA_FETCH_ERROR, {
type,
urn,
status,
message,
});

throw error;
}
};
13 changes: 13 additions & 0 deletions ws-nextjs-app/pages/[service]/live/[id]/LivePageLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import Stream from './Stream';
import Header from './Header';
import KeyPoints from './KeyPoints';
import HeadToHeadV2 from './SportDataHeader/head-to-head-v2';
import styles from './styles';
import { StreamResponse } from './Post/types';
import { KeyPointsResponse } from './KeyPoints/types';
Expand Down Expand Up @@ -59,6 +60,7 @@
endDateTime?: string;
metadata: { atiAnalytics: ATIData };
mediaCollections: MediaCollection[] | null;
sportEventDetails?: object | null;
};
};

Expand Down Expand Up @@ -87,8 +89,14 @@
headerImage,
promoImage,
mediaCollections,
sportEventDetails,
} = pageData;

// console.log('&&&&&&&&&&&&&&&&&&&&&');
// console.log('SPORT DATA');
// console.log(JSON.stringify(sportEventDetails));
// console.log('&&&&&&&&&&&&&&&&&&&&&');

const { currentStreamData, hasPendingUpdate, applyPendingUpdate } =
useLivePagePolling(pageData, livePagePollingEnabled && isLive);

Expand Down Expand Up @@ -179,6 +187,11 @@
imageWidth={imageWidth}
mediaCollections={mediaCollections}
/>
{sportEventDetails && (
<div>
<HeadToHeadV2 data={sportEventDetails?.event} />

Check failure on line 192 in ws-nextjs-app/pages/[service]/live/[id]/LivePageLayout.tsx

View workflow job for this annotation

GitHub Actions / cypress-run (22.x)

Property 'event' does not exist on type 'object'.

Check failure on line 192 in ws-nextjs-app/pages/[service]/live/[id]/LivePageLayout.tsx

View workflow job for this annotation

GitHub Actions / cypress-run (22.x)

Type '{ data: any; }' is missing the following properties from type '{ data: HeadToHeadV2Data; isConciseView: boolean; shouldHideBadges: boolean; shouldShowActions: boolean; maximumContainerScoreDigits?: string | undefined; teamBadgePlaceholderFallbackType?: "badge" | ... 1 more ... | undefined; }': isConciseView, shouldHideBadges, shouldShowActions

Check failure on line 192 in ws-nextjs-app/pages/[service]/live/[id]/LivePageLayout.tsx

View workflow job for this annotation

GitHub Actions / build (22.x)

Property 'event' does not exist on type 'object'.

Check failure on line 192 in ws-nextjs-app/pages/[service]/live/[id]/LivePageLayout.tsx

View workflow job for this annotation

GitHub Actions / build (22.x)

Type '{ data: any; }' is missing the following properties from type '{ data: HeadToHeadV2Data; isConciseView: boolean; shouldHideBadges: boolean; shouldShowActions: boolean; maximumContainerScoreDigits?: string | undefined; teamBadgePlaceholderFallbackType?: "badge" | ... 1 more ... | undefined; }': isConciseView, shouldHideBadges, shouldShowActions

Check failure on line 192 in ws-nextjs-app/pages/[service]/live/[id]/LivePageLayout.tsx

View workflow job for this annotation

GitHub Actions / cypress-run (22.x)

Property 'event' does not exist on type 'object'.

Check failure on line 192 in ws-nextjs-app/pages/[service]/live/[id]/LivePageLayout.tsx

View workflow job for this annotation

GitHub Actions / cypress-run (22.x)

Type '{ data: any; }' is missing the following properties from type '{ data: HeadToHeadV2Data; isConciseView: boolean; shouldHideBadges: boolean; shouldShowActions: boolean; maximumContainerScoreDigits?: string | undefined; teamBadgePlaceholderFallbackType?: "badge" | ... 1 more ... | undefined; }': isConciseView, shouldHideBadges, shouldShowActions
</div>
)}
<div css={styles.outerGrid}>
<div css={styles.firstSection}>
{keyPoints && (
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading