Skip to content

Commit

Permalink
Merge pull request #1337 from edenia/feat/add-structured-data-for-bps…
Browse files Browse the repository at this point in the history
…-1292

Feat/add structured data for bps 1292
  • Loading branch information
xavier506 committed Oct 15, 2023
2 parents 997c3ba + a53c31f commit 554f061
Show file tree
Hide file tree
Showing 13 changed files with 144 additions and 46 deletions.
4 changes: 2 additions & 2 deletions webapp/src/components/NodeCard/EndpointsChips.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,10 @@ const EndpointsChips = ({ node }) => {

return (
<>
<dt className={`${classes.bold} ${classes.endpointsTitle}`}>
<span className={`${classes.bold} ${classes.endpointsTitle}`}>
{t('endpoints')}
{!!node.endpoints.length && <EndpointsInfo status={status} />}
</dt>
</span>
<ChipList
list={node.endpoints.map(({ type, value }) => {
return (
Expand Down
2 changes: 1 addition & 1 deletion webapp/src/components/NodeCard/NodesCard.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ const NodesCard = ({ nodes, hideFeatures = false }) => {

return (
<>
<dt className={classes.bold}>{t('keys')}</dt>
<span className={classes.bold}>{t('keys')}</span>
{Object.keys(keys).map((key, i) => (
<div key={i} className={classes.keysContainer}>
<p className={classes.bold}>{key}:</p>
Expand Down
6 changes: 3 additions & 3 deletions webapp/src/components/NonCompliantCard/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { eosConfig, generalConfig } from '../../config'
import { formatWithThousandSeparator } from '../../utils'
import HealthCheck from '../HealthCheck'
import HealthCheckInfo from 'components/HealthCheck/HealthCheckInfo'
import isUrlValid from '../../utils/validate-url'
import isValidUrl from '../../utils/validate-url'
import VisitSite from 'components/VisitSite'

import styles from './styles'
Expand Down Expand Up @@ -67,7 +67,7 @@ const NonCompliantCard = ({ producer, stats }) => {
>
{t('website')}:
</Typography>
{isUrlValid(producer.url) ? (
{isValidUrl(producer.url) ? (
<>
<VisitSite title={t('openLink')} url={producer.url} />
<div className={classes.lightIcon}>
Expand All @@ -80,7 +80,7 @@ const NonCompliantCard = ({ producer, stats }) => {
<Typography variant="body1">{t('invalidUrl')}</Typography>
)}
</div>
{isUrlValid(producer.url) && (
{isValidUrl(producer.url) && (
<div className={classes.flex}>
<Typography
variant="body1"
Expand Down
4 changes: 2 additions & 2 deletions webapp/src/components/ProducersTable/ProducerRow.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ const ProducerRow = ({ producer, index }) => {
<>
<TableCell align="center">
<Typography className={classes.countryContainer}>
<CountryFlag code={producerOrg?.country} />
<CountryFlag code={producerOrg?.location?.country} />
<span className={classes.hideOnMobile}>
<br />
{producerOrg?.location}
{producerOrg?.location?.name}
</span>
</Typography>
</TableCell>
Expand Down
6 changes: 5 additions & 1 deletion webapp/src/hooks/customHooks/useBPLogoState.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ const useBPLogoState = (logo, defaultLogo) => {

const { naturalHeight, naturalWidth } = logoRef.current

if (naturalHeight < 100 || naturalHeight !== naturalWidth) {
const ratio =
Math.min(naturalHeight, naturalWidth) /
Math.max(naturalHeight, naturalWidth)

if (naturalHeight < 100 || naturalWidth < 100 || ratio < 0.8) {
setSrc(defaultLogo)
}
}
Expand Down
15 changes: 8 additions & 7 deletions webapp/src/routes/ProducerProfile/ProfileCard.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Typography from '@mui/material/Typography'
import Link from '@mui/material/Link'

import { eosConfig, generalConfig } from '../../config'
import isValidUrl from '../../utils/validate-url'
import ProducerName from 'components/ProducerName'
import ProducerSocialLinks from 'components/ProducerSocialLinks'
import LightIcon from 'components/HealthCheck/LightIcon'
Expand Down Expand Up @@ -71,8 +72,8 @@ const ProfileCard = ({ producer }) => {
title={t('location')}
value={
<>
{producer?.location}
<CountryFlag code={producer?.country} />{' '}
{producer?.location?.name}
<CountryFlag code={producer?.location?.country} />{' '}
</>
}
/>
Expand All @@ -84,7 +85,7 @@ const ProfileCard = ({ producer }) => {
/>
<OrganizationItem
title={t('website')}
value={producer?.media?.website}
value={isValidUrl(producer?.media?.website) ? producer?.media?.website : '' }
type="link"
/>
<OrganizationItem
Expand All @@ -102,7 +103,7 @@ const ProfileCard = ({ producer }) => {
value={producer?.info?.chainResources}
type="hiddenLink"
/>
{producer?.info?.otherResources?.length && (
{!!producer?.info?.otherResources?.length && (
<OrganizationItem
title={t('otherResources')}
value={producer?.info?.otherResources}
Expand All @@ -117,8 +118,8 @@ const ProfileCard = ({ producer }) => {
<>
<div className={classes.profile}>
<ProducerName
name={producer?.media?.name}
account={producer?.owner}
name={producer?.media?.name || producer?.owner}
account={producer?.media?.name ? producer?.owner : ''}
text={t('BPonNetwork', {
networkName: eosConfig.networkLabel,
position: producer?.media?.account,
Expand All @@ -127,7 +128,7 @@ const ProfileCard = ({ producer }) => {
lazy={false}
size="big"
/>
{producer?.social?.length && (
{!!producer?.social?.length && (
<div className={classes.socialLinks}>
<ProducerSocialLinks items={producer?.social} />
</div>
Expand Down
29 changes: 10 additions & 19 deletions webapp/src/routes/ProducerProfile/useProducerProfileState.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ import {
EOSRATE_STATS_QUERY,
} from '../../gql'
import { generalConfig } from '../../config'
import { formatData } from '../../utils/formatData'
import isValidAccountName from 'utils/validate-account-name'
import sortNodes from 'utils/sort-nodes'
import {
getBPStructuredData,
formatData,
sortNodes,
isValidAccountName,
} from '../../utils'

const useProducerProfileState = (name, previousData) => {
const defaultVariables = {
Expand Down Expand Up @@ -102,22 +105,10 @@ const useProducerProfileState = (name, previousData) => {
}, [producers])

useEffect(() => {
if (!producer || !Object?.keys(producer).length) return

setLdJson(
JSON.stringify({
'@context': 'http://schema.org',
'@type': 'Organization',
name: producer?.media?.name,
url: producer?.media?.website,
contactPoint: {
'@type': 'ContactPoint',
email: producer?.media?.email,
contactType: 'customer service',
},
logo: producer?.media?.logo,
}),
)
if (!producer || !Object?.keys(producer).length || !producer?.media?.name)
return

setLdJson(prev => prev || JSON.stringify(getBPStructuredData(producer)))
}, [producer])

return [{ loading, producer, ldJson }, {}]
Expand Down
3 changes: 1 addition & 2 deletions webapp/src/utils/formatData.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,7 @@ export const formatData = ({
website: data?.website || '',
email: data?.email,
},
location: data?.location?.name || 'N/A',
country: data?.location?.country || null,
location: data?.location,
info: {
codeOfConduct: data?.code_of_conduct,
ownership: data?.ownership_disclosure,
Expand Down
100 changes: 100 additions & 0 deletions webapp/src/utils/get-bp-structured-data.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { eosConfig } from '../config'

const getNodesStructuredData = producer => {
if (!producer.bp_json?.nodes?.length) return []

const structuredData = { nodes: [], inserted: [] }
const nodesTypes = [
{
type: 'seed',
endpoint: 'p2p_endpoint',
name: `${eosConfig.networkLabel} P2P Endpoint`,
description: 'P2P connectivity endpoints',
},
{
type: 'query',
endpoint: 'ssl_endpoint',
name: `${eosConfig.networkLabel} API Endpoint`,
description: 'HTTPS API access point',
},
]

for (const node of producer.bp_json?.nodes) {
if (structuredData.inserted.includes(node.node_type)) continue

const nodeType = nodesTypes.find(
item => item.type === node.node_type && node[item.endpoint],
)

if (nodeType) {
structuredData.inserted.push(node.node_type)
structuredData.nodes.push({
'@type': 'webAPI',
name: nodeType.name,
description: nodeType.description,
url: node[nodeType.endpoint],
provider: {
'@type': 'Organization',
name: producer?.media?.name,
},
})
}

if (structuredData.inserted.length >= nodesTypes.length) {
break
}
}

return structuredData.nodes
}

export const getBPStructuredData = producer => {
const sameAs = producer.social?.map(socialMedia => socialMedia.url)
const hasValidLocation = Object.keys(producer.location || {}).every(
(key) => !!producer.location[key],
)

if (producer.info?.codeOfConduct) {
sameAs.push(producer.info?.codeOfConduct)
}

if (producer.info?.ownership) {
sameAs.push(producer.info?.ownership)
}

const owns = getNodesStructuredData(producer)

return {
'@context': 'http://schema.org',
'@type': 'Organization',
name: producer?.media?.name,
url: producer?.media?.website,
...(producer?.media?.logo && { logo: producer.media?.logo }),
...(producer.media?.email && {
contactPoint: {
'@type': 'ContactPoint',
email: producer?.media?.email,
contactType: 'customer service',
},
}),
...(hasValidLocation && {
location: {
'@type': 'Place',
address: {
'@type': 'PostalAddress',
addressLocality: producer.location.name,
addressCountry: producer.location.country,
},
geo: {
'@type': 'GeoCoordinates',
latitude: producer.location.latitude,
longitude: producer.location.longitude,
},
},
}),
...(sameAs.length && { sameAs }),
...(owns.length && { owns }),
}
}

export default getBPStructuredData
11 changes: 7 additions & 4 deletions webapp/src/utils/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
export * from './format-with-thousand-separator'
export * from './on-img-error'
export * from './eos'
export * from './format-with-thousand-separator'
export * from './formatData'
export * from './get-blocknum-url'
export * from './get-bp-structured-data'
export * from './get-endpoint-health-status'
export * from './get-evmblocknum-url'
export * from './get-range-options'
export * from './get-transaction-url'
export * from './on-img-error'
export * from './sort-nodes'
export * from './validate-account-name'
export * from './validate-url'
export * from './get-evmblocknum-url'
export * from './get-endpoint-health-status'
4 changes: 2 additions & 2 deletions webapp/src/utils/sort-nodes.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const getOrderNode = (node) => {
const getOrderNode = node => {
return (
(node.type?.includes('full') ? 0.5 : 0) +
(node.endpoints?.length || 0) +
Expand All @@ -7,7 +7,7 @@ const getOrderNode = (node) => {
)
}

const sortNodes = (unsortedNodes, key) => {
export const sortNodes = (unsortedNodes, key) => {
let nodes = []
let producerNode

Expand Down
2 changes: 1 addition & 1 deletion webapp/src/utils/validate-account-name.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const isValidAccountName = name => {
export const isValidAccountName = name => {
const regex = /^[.12345abcdefghijklmnopqrstuvwxyz]+$/

return name?.length < 13 && regex.test(name)
Expand Down
4 changes: 2 additions & 2 deletions webapp/src/utils/validate-url.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
const isUrlValid = (url) => {
const isValidUrl = url => {
const urlRegex =
/(http|https):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])/

return url && urlRegex.test(url)
}

export default isUrlValid
export default isValidUrl

0 comments on commit 554f061

Please sign in to comment.