diff --git a/.github/workflows/__check-action.yml b/.github/workflows/__check-action.yml index 7c0192d..2a46249 100644 --- a/.github/workflows/__check-action.yml +++ b/.github/workflows/__check-action.yml @@ -106,6 +106,7 @@ jobs: Agenda: Speaker "Wrong Speaker" is not in the list of speakers Agenda: Entry "- Wrong agenda entry" must follow the format: "- : " Meetup Link: Must be a valid Meetup link, e.g. https://www.meetup.com/fr-FR/devops-aix-marseille/events/123456789 + CNCF Link: Must be a valid CNCF link, e.g. https://community.cncf.io/events/details/cncf-cloud-native-aix-marseille-presents-quiz-conteneurs-et-observabilite Drive Link: Must be a valid Drive Link, e.g. https://drive.google.com/drive/folders/1a2b3c4d5e6f7g8h9i0j EOF ) diff --git a/__tests__/meetup-issue-template.yml b/__tests__/meetup-issue-template.yml index 089241e..48f795f 100644 --- a/__tests__/meetup-issue-template.yml +++ b/__tests__/meetup-issue-template.yml @@ -1,115 +1,120 @@ -name: Meetup -description: Gather meetup event organization data -title: "[Meetup]" -labels: - - meetup -body: - - type: input - id: event_title - attributes: - label: Event Title - description: Provide the title of the event - placeholder: Enter the event title - - type: input - id: event_date - attributes: - label: Event date - description: | - Specify the proposed date for the event. - Format: YYYY-MM-DD - placeholder: YYYY-MM-DD - - type: dropdown - id: hoster - attributes: - label: Hosting - description: Pick a hoster - options: - - EasyPartner - - Sept Lieues - - Axians Communication & Cloud Aix - - FGTECH - - Softway medical - - Free Pro - - La Briqueterie - - Devoteam - - SmartTribune - - OnePoint - - Linxo - - Squad - - Doinsport - - Numbr Marseille - - La plateforme - - Smile - - TheCamp - - type: textarea - id: event_description - attributes: - label: Event Description - description: Short describe the event - - type: textarea - id: agenda - attributes: - label: Agenda - description: Outline the schedule and topics to be discussed - placeholder: | - - : - - : - - type: input - id: meetup_link - attributes: - label: Meetup Link - description: Provide the link to the Meetup event page - placeholder: https://www.meetup.com/fr-FR/devops-aix-marseille/events/xxxxxxx - - type: input - id: drive_link - attributes: - label: Drive Link - description: Provide the link to the Google Drive folder - placeholder: https://drive.google.com/drive/folders/xxxxxxxxxxxxxxxxxxxx - - type: checkboxes - id: slides - attributes: - label: Slides & Content - description: Check items for slides and event content - options: - - label: Basic Slides - - label: Announcements / surveys / news etc... - - label: Slides hosting - - label: Slides speaker(s) - - label: Kahoot / Game - - type: checkboxes - id: communication - attributes: - label: Communication - description: Select communication items to be prepared - options: - - label: Announcement - - label: Cover image - - label: Social networks - - label: Goodies - - type: dropdown - id: aperitif - attributes: - label: Aperitif - description: To be provided? - options: +"name": "Meetup" +"description": "Gather meetup event organization data" +"title": "[Meetup]" +"labels": + - "meetup" +"body": + - "type": "input" + "id": "event_title" + "attributes": + "label": "Event Title" + "description": "Provide the title of the event" + "placeholder": "Enter the event title" + - "type": "input" + "id": "event_date" + "attributes": + "label": "Event date" + "description": "Specify the proposed date for the event. + + Format: YYYY-MM-DD\n" + "placeholder": "YYYY-MM-DD" + - "type": "dropdown" + "id": "hoster" + "attributes": + "label": "Hosting" + "description": "Pick a hoster" + "options": + - "Hoster 1" + - "Hoster 2" + - "type": "textarea" + "id": "event_description" + "attributes": + "label": "Event Description" + "description": "Short describe the event" + - "type": "markdown" + "attributes": + "value": " + + " + - "type": "textarea" + "id": "agenda" + "attributes": + "label": "Agenda" + "description": "Outline the schedule and topics to be discussed" + "placeholder": "- : + + - : \n" + - "type": "input" + "id": "meetup_link" + "attributes": + "label": "Meetup Link" + "description": "Provide the link to the Meetup event page" + "placeholder": "https://www.meetup.com/fr-FR/devops-aix-marseille/events/xxxxxxx" + - "type": "input" + "id": "cncf_link" + "attributes": + "label": "CNCF Link" + "description": "Provide the link to the CNCF event page" + "placeholder": "https://community.cncf.io/events/details/cncf-cloud-native-aix-marseille-presents-xxx" + - "type": "input" + "id": "drive_link" + "attributes": + "label": "Drive Link" + "description": "Provide the link to the Google Drive folder" + "placeholder": "https://drive.google.com/drive/folders/xxxxxxxxxxxxxxxxxxxx" + - "type": "checkboxes" + "id": "slides" + "attributes": + "label": "Slides & Content" + "description": "Check items for slides and event content" + "options": + - "label": "Cover image" + - "label": "Basic Slides" + - "label": "Announcements / surveys / news etc..." + - "label": "Slides hosting" + - "label": "Slides speaker(s)" + - "label": "Kahoot / Game" + - "label": "Openfeedback" + - "label": "Goodies" + - "type": "checkboxes" + "id": "communication" + "attributes": + "label": "Communication" + "description": "Select communication items to be prepared" + "options": + - "label": "Announcement" + - "label": "Cover image" + - "label": "Slack - AixMarseille.Tech" + - "label": "Slack - OpenBaR" + - "label": "LinkedIn" + - "label": "BlueSky" + - "type": "dropdown" + "id": "aperitif" + "attributes": + "label": "Aperitif" + "description": "To be provided?" + "options": - "Yes" - "No" - - type: dropdown - id: restaurant - attributes: - label: Restaurant / Bar - description: Afterwards? - options: + - "type": "dropdown" + "id": "restaurant" + "attributes": + "label": "Restaurant / Bar" + "description": "Afterwards?" + "options": - "Yes" - "No" - - type: checkboxes - id: post-event - attributes: - label: Post event - description: Select Post event items to be done - options: - - label: Thanks hoster - - label: Thanks speakers - - label: Share slides to meetup - - label: Social networks + - "type": "checkboxes" + "id": "post-event" + "attributes": + "label": "Post event" + "description": "Select Post event items to be done" + "options": + - "label": "Thanks hoster" + - "label": "Thanks speakers" + - "label": "Share slides to Meetup" + - "label": "Social networks" diff --git a/dist/index.js b/dist/index.js index 1c49edf..5e74d75 100644 --- a/dist/index.js +++ b/dist/index.js @@ -34762,6 +34762,7 @@ const hoster_linter_adapter_1 = __nccwpck_require__(5057); const agenda_linter_adapter_1 = __nccwpck_require__(3066); const meetup_link_linter_adapter_1 = __nccwpck_require__(4595); const drive_link_linter_adapter_1 = __nccwpck_require__(3271); +const cncf_link_linter_adapter_1 = __nccwpck_require__(8321); const container = new inversify_1.Container(); exports.container = container; container.bind(core_service_1.CORE_SERVICE_IDENTIFIER).toConstantValue(core_service_1.coreService); @@ -34778,6 +34779,7 @@ container.bind(linter_adapter_1.LINTER_ADAPTER_IDENTIFIER).to(hoster_linter_adap container.bind(linter_adapter_1.LINTER_ADAPTER_IDENTIFIER).to(event_description_linter_adapter_1.EventDescriptionLinterAdapter); container.bind(linter_adapter_1.LINTER_ADAPTER_IDENTIFIER).to(agenda_linter_adapter_1.AgendaLinterAdapter); container.bind(linter_adapter_1.LINTER_ADAPTER_IDENTIFIER).to(meetup_link_linter_adapter_1.MeetupLinkLinterAdapter); +container.bind(linter_adapter_1.LINTER_ADAPTER_IDENTIFIER).to(cncf_link_linter_adapter_1.CNCFLinkLinterAdapter); container.bind(linter_adapter_1.LINTER_ADAPTER_IDENTIFIER).to(drive_link_linter_adapter_1.DriveLinkLinterAdapter); @@ -34989,6 +34991,43 @@ exports.AgendaLinterAdapter = AgendaLinterAdapter = AgendaLinterAdapter_1 = __de ], AgendaLinterAdapter); +/***/ }), + +/***/ 8321: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var CNCFLinkLinterAdapter_1; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.CNCFLinkLinterAdapter = void 0; +const inversify_1 = __nccwpck_require__(4871); +const zod_1 = __nccwpck_require__(4809); +const abtract_zod_linter_adapter_1 = __nccwpck_require__(4053); +let CNCFLinkLinterAdapter = class CNCFLinkLinterAdapter extends abtract_zod_linter_adapter_1.AbstractZodLinterAdapter { + static { CNCFLinkLinterAdapter_1 = this; } + static CNCF_LINK_REGEX = /^https:\/\/community\.cncf\.io\/events\/details\/cncf-cloud-native-aix-marseille-presents-[0-9a-z-]+$/; + getValidator() { + return (0, zod_1.string)().url().regex(CNCFLinkLinterAdapter_1.CNCF_LINK_REGEX, { + message: "Must be a valid CNCF link, e.g. https://community.cncf.io/events/details/cncf-cloud-native-aix-marseille-presents-quiz-conteneurs-et-observabilite", + }); + } + getFieldName() { + return "cncf_link"; + } +}; +exports.CNCFLinkLinterAdapter = CNCFLinkLinterAdapter; +exports.CNCFLinkLinterAdapter = CNCFLinkLinterAdapter = CNCFLinkLinterAdapter_1 = __decorate([ + (0, inversify_1.injectable)() +], CNCFLinkLinterAdapter); + + /***/ }), /***/ 3271: @@ -35752,6 +35791,7 @@ exports.MEETUP_ISSUE_BODY_FIELD_LABELS = { event_description: "Event Description", agenda: "Agenda", meetup_link: "Meetup Link", + cncf_link: "CNCF Link", drive_link: "Drive Link", }; let MeetupIssueService = class MeetupIssueService { diff --git a/src/__fixtures__/hosters.fixture.ts b/src/__fixtures__/hosters.fixture.ts index 1fefd6c..706e3c6 100644 --- a/src/__fixtures__/hosters.fixture.ts +++ b/src/__fixtures__/hosters.fixture.ts @@ -1,3 +1,3 @@ export function getHostersFixture(): [string, ...string[]] { - return ["hoster 1", "hoster 2"]; + return ["Hoster 1", "Hoster 2"]; } diff --git a/src/__fixtures__/meetup-issue.fixture.ts b/src/__fixtures__/meetup-issue.fixture.ts index 660e6d5..918a8ce 100644 --- a/src/__fixtures__/meetup-issue.fixture.ts +++ b/src/__fixtures__/meetup-issue.fixture.ts @@ -16,6 +16,8 @@ export function getMeetupIssueFixture(override?: Partial): MeetupIs event_description: "Description", agenda: `- ${speakers[0]}: Talk description One\n- ${speakers[1]}: Talk description Two`, meetup_link: "https://www.meetup.com/fr-FR/devops-aix-marseille/events/123456789", + cncf_link: + "https://community.cncf.io/events/details/cncf-cloud-native-aix-marseille-presents-quiz-conteneurs-et-observabilite", drive_link: "https://drive.google.com/drive/folders/1a2b3c4d5e6f7g8h9i0j", ...(override?.body ?? {}), }, diff --git a/src/container.ts b/src/container.ts index b2cdc67..cc4db5b 100644 --- a/src/container.ts +++ b/src/container.ts @@ -14,6 +14,7 @@ import { HosterLinterAdapter } from "./linter/adapter/hoster-linter.adapter"; import { AgendaLinterAdapter } from "./linter/adapter/agenda-linter.adapter"; import { MeetupLinkLinterAdapter } from "./linter/adapter/meetup-link-linter.adapter"; import { DriveLinkLinterAdapter } from "./linter/adapter/drive-link-linter.adapter"; +import { CNCFLinkLinterAdapter } from "./linter/adapter/cncf-link-linter.adapter"; const container = new Container(); @@ -33,6 +34,7 @@ container.bind(LINTER_ADAPTER_IDENTIFIER).to(HosterLinterAdapter) container.bind(LINTER_ADAPTER_IDENTIFIER).to(EventDescriptionLinterAdapter); container.bind(LINTER_ADAPTER_IDENTIFIER).to(AgendaLinterAdapter); container.bind(LINTER_ADAPTER_IDENTIFIER).to(MeetupLinkLinterAdapter); +container.bind(LINTER_ADAPTER_IDENTIFIER).to(CNCFLinkLinterAdapter); container.bind(LINTER_ADAPTER_IDENTIFIER).to(DriveLinkLinterAdapter); export { container }; diff --git a/src/linter/adapter/cncf-link-linter.adapter.test.ts b/src/linter/adapter/cncf-link-linter.adapter.test.ts new file mode 100644 index 0000000..8010d72 --- /dev/null +++ b/src/linter/adapter/cncf-link-linter.adapter.test.ts @@ -0,0 +1,63 @@ +import { CNCFLinkLinterAdapter } from "./cncf-link-linter.adapter"; +import { LintError } from "../lint.error"; +import { getMeetupIssueFixture } from "../../__fixtures__/meetup-issue.fixture"; + +describe("CNCFLinkLinterAdapter", () => { + let cncfLinkLinterAdapter: CNCFLinkLinterAdapter; + + beforeEach(() => { + cncfLinkLinterAdapter = new CNCFLinkLinterAdapter(); + }); + + describe("lint", () => { + it("should return the cncf issue if the CNCF link is valid", async () => { + // Arrange + const meetupIssue = getMeetupIssueFixture(); + const shouldFix = false; + + // Act + const result = await cncfLinkLinterAdapter.lint(meetupIssue, shouldFix); + + // Assert + expect(result).toEqual(meetupIssue); + }); + + it("should throw a LintError if the CNCF link is invalid", async () => { + // Arrange + const invalidmeetupIssue = getMeetupIssueFixture({ + body: { + cncf_link: "invalid-link", + }, + }); + const shouldFix = false; + + // Act & Assert + const expectedError = new LintError([ + "CNCF Link: Invalid url; Must be a valid CNCF link, e.g. https://community.cncf.io/events/details/cncf-cloud-native-aix-marseille-presents-quiz-conteneurs-et-observabilite", + ]); + + await expect(cncfLinkLinterAdapter.lint(invalidmeetupIssue, shouldFix)).rejects.toStrictEqual( + expectedError + ); + }); + + it("should throw a LintError if the CNCF link is not a CNCF link", async () => { + // Arrange + const invalidmeetupIssue = getMeetupIssueFixture({ + body: { + cncf_link: "https://www.google.com", + }, + }); + const shouldFix = false; + + // Act & Assert + const expectedError = new LintError([ + "CNCF Link: Must be a valid CNCF link, e.g. https://community.cncf.io/events/details/cncf-cloud-native-aix-marseille-presents-quiz-conteneurs-et-observabilite", + ]); + + await expect(cncfLinkLinterAdapter.lint(invalidmeetupIssue, shouldFix)).rejects.toStrictEqual( + expectedError + ); + }); + }); +}); diff --git a/src/linter/adapter/cncf-link-linter.adapter.ts b/src/linter/adapter/cncf-link-linter.adapter.ts new file mode 100644 index 0000000..3c2473c --- /dev/null +++ b/src/linter/adapter/cncf-link-linter.adapter.ts @@ -0,0 +1,20 @@ +import { injectable } from "inversify"; +import { string } from "zod"; +import { AbstractZodLinterAdapter } from "./abtract-zod-linter.adapter"; + +@injectable() +export class CNCFLinkLinterAdapter extends AbstractZodLinterAdapter { + private static readonly CNCF_LINK_REGEX = + /^https:\/\/community\.cncf\.io\/events\/details\/cncf-cloud-native-aix-marseille-presents-[0-9a-z-]+$/; + + protected getValidator() { + return string().url().regex(CNCFLinkLinterAdapter.CNCF_LINK_REGEX, { + message: + "Must be a valid CNCF link, e.g. https://community.cncf.io/events/details/cncf-cloud-native-aix-marseille-presents-quiz-conteneurs-et-observabilite", + }); + } + + protected getFieldName() { + return "cncf_link" as const; + } +} diff --git a/src/services/meetup-issue.service.ts b/src/services/meetup-issue.service.ts index f8a1c95..2d2f747 100644 --- a/src/services/meetup-issue.service.ts +++ b/src/services/meetup-issue.service.ts @@ -11,6 +11,7 @@ export type MeetupIssueBody = { event_description?: string; agenda?: string; meetup_link?: string; + cncf_link?: string; drive_link?: string; }; @@ -28,6 +29,7 @@ export const MEETUP_ISSUE_BODY_FIELD_LABELS: Record