Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add renderDateBadge helper; affects [aur BitbucketLastCommit chrome date eclipse factorio galaxytoolshed GiteaLastCommit GistLastCommit GithubCreatedAt GithubHacktoberfest GithubIssueDetail GithubLastCommit GithubReleaseDate GitlabLastCommit maven npm openvsx snapcraft SourceforgeLastCommit steam vaadin visualstudio wordpress] #10682

Merged
merged 5 commits into from
Nov 17, 2024
16 changes: 4 additions & 12 deletions services/aur/aur.service.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import Joi from 'joi'
import {
floorCount as floorCountColor,
age as ageColor,
} from '../color-formatters.js'
import { renderDateBadge } from '../date.js'
import { floorCount as floorCountColor } from '../color-formatters.js'
import { renderLicenseBadge } from '../licenses.js'
import { metric, formatDate } from '../text-formatters.js'
import { metric } from '../text-formatters.js'
import { nonNegativeInteger } from '../validators.js'
import {
BaseJsonService,
Expand Down Expand Up @@ -243,16 +241,10 @@ class AurLastModified extends BaseAurService {

static defaultBadgeData = { label: 'last modified' }

static render({ date }) {
const color = ageColor(date)
const message = formatDate(date)
return { color, message }
}

async handle({ packageName }) {
const json = await this.fetch({ packageName })
const date = 1000 * parseInt(json.results[0].LastModified)
return this.constructor.render({ date })
return renderDateBadge(date)
}
}

Expand Down
12 changes: 2 additions & 10 deletions services/bitbucket/bitbucket-last-commit.service.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import Joi from 'joi'
import { age as ageColor } from '../color-formatters.js'
import { renderDateBadge } from '../date.js'
import { BaseJsonService, NotFound, pathParam, queryParam } from '../index.js'
import { formatDate } from '../text-formatters.js'
import { relativeUri } from '../validators.js'

const schema = Joi.object({
Expand Down Expand Up @@ -43,13 +42,6 @@ export default class BitbucketLastCommit extends BaseJsonService {

static defaultBadgeData = { label: 'last commit' }

static render({ commitDate }) {
return {
message: formatDate(commitDate),
color: ageColor(Date.parse(commitDate)),
}
}

async fetch({ user, repo, branch, path }) {
// https://developer.atlassian.com/cloud/bitbucket/rest/api-group-commits/#api-repositories-workspace-repo-slug-commits-get
return this._requestJson({
Expand All @@ -76,6 +68,6 @@ export default class BitbucketLastCommit extends BaseJsonService {

if (!commit) throw new NotFound({ prettyMessage: 'no commits found' })

return this.constructor.render({ commitDate: commit.date })
return renderDateBadge(commit.date)
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { age } from '../color-formatters.js'
import { formatDate } from '../text-formatters.js'
import { renderDateBadge } from '../date.js'
import { NotFound, pathParams } from '../index.js'
import BaseChromeWebStoreService from './chrome-web-store-base.js'

Expand Down Expand Up @@ -31,11 +30,6 @@ export default class ChromeWebStoreLastUpdated extends BaseChromeWebStoreService
throw new NotFound({ prettyMessage: 'not found' })
}

const lastUpdatedDate = Date.parse(lastUpdated)

return {
message: formatDate(lastUpdatedDate),
color: age(lastUpdatedDate),
}
return renderDateBadge(lastUpdated)
}
}
18 changes: 0 additions & 18 deletions services/color-formatters.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
*/

import pep440 from '@renovatebot/pep440'
import dayjs from 'dayjs'

/**
* Determines the color used for a badge based on version.
Expand Down Expand Up @@ -175,24 +174,7 @@ function colorScale(steps, colors, reversed) {
}
}

/**
* Determines the color used for a badge according to the age.
* Age is calculated as days elapsed till current date.
* The color varies from bright green to red as the age increases
* or the other way around if `reverse` is given `true`.
*
* @param {string} date Date string
* @param {boolean} reversed Reverse the color scale a.k.a. the older, the better
* @returns {string} Badge color
*/
function age(date, reversed = false) {
const colorByAge = colorScale([7, 30, 180, 365, 730], undefined, !reversed)
const daysElapsed = dayjs().diff(dayjs(date), 'days')
return colorByAge(daysElapsed)
}

export {
age,
colorScale,
coveragePercentage,
downloadCount,
Expand Down
41 changes: 0 additions & 41 deletions services/color-formatters.spec.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { expect } from 'chai'
import { forCases, given, test } from 'sazerac'
import {
age,
colorScale,
coveragePercentage,
letterScore,
Expand Down Expand Up @@ -53,46 +52,6 @@ describe('Color formatters', function () {
given('Z').expect('red')
})

const monthsAgo = months => {
const result = new Date()
// This looks wack but it works.
result.setMonth(result.getMonth() - months)
return result
}
test(age, () => {
given(Date.now())
.describe('when given the current timestamp')
.expect('brightgreen')
given(new Date())
.describe('when given the current Date')
.expect('brightgreen')
given(new Date(2001, 1, 1))
.describe('when given a Date many years ago')
.expect('red')
given(monthsAgo(2))
.describe('when given a Date two months ago')
.expect('yellowgreen')
given(monthsAgo(15))
.describe('when given a Date 15 months ago')
.expect('orange')
// --- reversed --- //
given(Date.now(), true)
.describe('when given the current timestamp and reversed')
.expect('red')
given(new Date(), true)
.describe('when given the current Date and reversed')
.expect('red')
given(new Date(2001, 1, 1), true)
.describe('when given a Date many years ago and reversed')
.expect('brightgreen')
given(monthsAgo(2), true)
.describe('when given a Date two months ago and reversed')
.expect('yellow')
given(monthsAgo(15), true)
.describe('when given a Date 15 months ago and reversed')
.expect('green')
})

test(version, () => {
forCases([given('1.0'), given(9), given(1.0)]).expect('blue')

Expand Down
108 changes: 108 additions & 0 deletions services/date.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/**
* Commonly-used functions for rendering badges containing a date
*
* @module
*/

import dayjs from 'dayjs'
import calendar from 'dayjs/plugin/calendar.js'
import customParseFormat from 'dayjs/plugin/customParseFormat.js'
import { colorScale } from './color-formatters.js'
import { InvalidResponse } from './index.js'

dayjs.extend(calendar)
dayjs.extend(customParseFormat)

/**
* Parse and validate a string date into a dayjs object. Use this helper
* in preference to invoking dayjs directly when parsing a date from string.
*
* @param {...any} args - Variadic: Arguments to pass through to dayjs
* @returns {dayjs} - Parsed object
* @throws {InvalidResponse} - Error if validation fails
* @see https://day.js.org/docs/en/parse/string
* @see https://day.js.org/docs/en/parse/string-format
* @see https://day.js.org/docs/en/parse/is-valid
* @example
* parseDate('2024-01-01')
* parseDate('31/01/2024', 'DD/MM/YYYY')
* parseDate('2018 Enero 15', 'YYYY MMMM DD', 'es')
*/
function parseDate(...args) {
let date
if (args.length >= 2) {
// always use strict mode if format arg is supplied
date = dayjs(...args, true)
} else {
date = dayjs(...args)
}
if (!date.isValid()) {
throw new InvalidResponse({ prettyMessage: 'invalid date' })
}
return date
}

/**
* Returns a formatted date string without the year based on the value of input date param d.
*
* @param {Date | string | number | dayjs } d JS Date object, string, unix timestamp or dayjs object
* @returns {string} Formatted date string
*/
function formatDate(d) {
const date = parseDate(d)
const dateString = date.calendar(null, {
lastDay: '[yesterday]',
sameDay: '[today]',
lastWeek: '[last] dddd',
sameElse: 'MMMM YYYY',
})
// Trim current year from date string
return dateString.replace(` ${dayjs().year()}`, '').toLowerCase()
}

/**
* Determines the color used for a badge according to the age.
* Age is calculated as days elapsed till current date.
* The color varies from bright green to red as the age increases
* or the other way around if `reverse` is given `true`.
*
* @param {Date | string | number | dayjs } date JS Date object, string, unix timestamp or dayjs object
* @param {boolean} reversed Reverse the color scale (the older, the better)
* @returns {string} Badge color
*/
function age(date, reversed = false) {
const colorByAge = colorScale([7, 30, 180, 365, 730], undefined, !reversed)
const daysElapsed = dayjs().diff(parseDate(date), 'days')
return colorByAge(daysElapsed)
}

/**
* Creates a badge object that displays a date
*
* @param {Date | string | number | dayjs } date JS Date object, string, unix timestamp or dayjs object
* @param {boolean} reversed Reverse the color scale (the older, the better)
* @returns {object} A badge object that has two properties: message, and color
*/
function renderDateBadge(date, reversed = false) {
const d = parseDate(date)
chris48s marked this conversation as resolved.
Show resolved Hide resolved
const color = age(d, reversed)
const message = formatDate(d)
return { message, color }
}

/**
* Returns a relative date from the input timestamp.
* For example, day after tomorrow's timestamp will return 'in 2 days'.
*
* @param {number | string} timestamp - Unix timestamp
* @returns {string} Relative date from the unix timestamp
*/
function formatRelativeDate(timestamp) {
const parsedDate = dayjs.unix(parseInt(timestamp, 10))
if (!parsedDate.isValid()) {
return 'invalid date'
}
return dayjs().to(parsedDate).toLowerCase()
}

export { parseDate, renderDateBadge, formatDate, formatRelativeDate, age }
Loading
Loading