Сегодняшний пост не обязательно слишком интересен с точки зрения кода, но он затрагивает некоторые более важные, более широкие темы без сервера, которые я хотел бы затронуть. Несколько недель назад я обнаружил интересный репозиторий GitHub: https://github.com/gadael/icsdb.

Этот репозиторий содержит файлы iCal (например, данные календаря в виде простого текста) для нерабочих дней для всех 50 штатов США и различных европейских стран. Это может быть полезно во многих отношениях, если, конечно, вы можете анализировать данные iCal. Я подумал, что может быть интересно создать простую службу, которая будет принимать URL-адрес, указывающий на данные iCal, и возвращать информацию в форме JSON.

Существует несколько различных способов анализа iCal, но я считаю, что ical.js от Mozilla достаточно хорош. Библиотека ical.js довольно сложна и позволяет вам работать с данными iCal как с компонентом, вызывая различные методы для поиска данных, но она также может просто возвращать простую проанализированную версию текста. Вот код, который я придумал:

const rp = require('request-promise');
const ical = require('ical.js');

function flattenEvent(e) {
    let event = {};
    for(let i=0;i<e[1].length;i++) {
        let prop = e[1][i];
        event[prop[0]] = prop[3];
        //console.log('e',prop);
    } 
    return event;
}

exports.main = (args) => {

    return new Promise((resolve, reject) => {
        rp(args.url).then((txt) => {
            try {
                let parsed = ical.parse(txt);
                let events = parsed[2];

                let result = [];
                events.forEach(e => result.push(flattenEvent(e)));
                resolve({events:result});
            } catch(e) {
                console.log(e);
                reject(e.message);
            }
        })
        .catch((e) => {
            reject(e);  
        });
    });
}

По сути — высосать удаленный URL и разобрать с помощью библиотеки. Как я уже сказал, есть хороший объектно-ориентированный API, который предоставляет библиотека, но я обнаружил, что могу работать с исходными данными немного проще. Вы можете видеть, где я просто беру третий элемент в массиве, чтобы получить фактические события. Затем я выравниваю данные, используя flattenEvent. Если вам интересно, данные представлены в форме под названием jCal, которая имеет спецификацию: https://tools.ietf.org/html/draft-ietf-jcardcal-jcal-10. Моя функция flattenEvent делает некоторые допущения, которые не обязательно всегда работают хорошо, но пока все работает нормально.

Я проверил это, попросив отпуск в Луизиане:

wsk action invoke ical/get --param url https://raw.githubusercontent.com/gadael/icsdb/master/build/en-US/us-louisiana-nonworkingdays.ics -r

А вот и первые пять результатов.

{
    "class": "PUBLIC",
    "created": "2014-01-09T00:47:56Z",
    "description": "",
    "dtend": "1970-01-02",
    "dtstamp": "2017-06-23T13:53:48Z",
    "dtstart": "1970-01-01",
    "last-modified": "2017-06-23T13:53:48Z",
    "rrule": {
        "freq": "YEARLY"
    },
    "sequence": 0,
    "status": "CONFIRMED",
    "summary": "New Year's Day",
    "transp": "TRANSPARENT",
    "uid": "b901ca08-d924-43c3-9166-1d215c9453d6"
},
{
    "class": "PUBLIC",
    "created": "2014-01-09T00:47:56Z",
    "description": "",
    "dtend": "1983-01-02",
    "dtstamp": "2017-06-23T13:53:48Z",
    "dtstart": "1983-01-01",
    "last-modified": "2017-06-23T13:53:48Z",
    "rrule": {
        "byday": "3MO",
        "freq": "YEARLY"
    },
    "sequence": 0,
    "status": "CONFIRMED",
    "summary": "Marthin Luther King day/Robert E. Lee day",
    "transp": "TRANSPARENT",
    "uid": "0ae8128a-e360-492c-b2bd-52ed0d6d06fd"
},
{
    "categories": "-New Mexico",
    "class": "PUBLIC",
    "created": "2014-01-09T00:47:56Z",
    "description": "",
    "dtend": "1970-02-01",
    "dtstamp": "2017-06-23T13:53:48Z",
    "dtstart": "1970-02-01",
    "last-modified": "2017-06-23T13:53:48Z",
    "rrule": {
        "byday": "3MO",
        "freq": "YEARLY"
    },
    "sequence": 0,
    "status": "CONFIRMED",
    "summary": "Presidents Day",
    "transp": "TRANSPARENT",
    "uid": "17425d41-9ed3-4088-adad-4693d1bd44c9"
},
{
    "categories": "Louisiana",
    "class": "PUBLIC",
    "created": "2014-01-09T00:47:56Z",
    "description": "",
    "dtend": "1970-04-02",
    "dtstamp": "2017-06-23T13:53:48Z",
    "dtstart": "1970-04-01",
    "last-modified": "2017-06-23T13:53:48Z",
    "rdate": "1970-02-10",
    "sequence": 0,
    "status": "CONFIRMED",
    "summary": "Mardi gras",
    "transp": "TRANSPARENT",
    "uid": "7ac45e93-a684-4061-a0c7-948a89e358b0"
},
{
    "categories": "Connecticut",
    "class": "PUBLIC",
    "created": "2014-01-09T00:47:56Z",
    "description": "",
    "dtend": "1970-04-09",
    "dtstamp": "2017-06-23T13:53:48Z",
    "dtstart": "1970-04-08",
    "last-modified": "2017-06-23T13:53:48Z",
    "rdate": "1970-03-26",
    "sequence": 0,
    "status": "CONFIRMED",
    "summary": "Good Friday",
    "transp": "TRANSPARENT",
    "uid": "3c46243f-00f8-418f-94cf-4eda72ae7cb2"
}

Итак... некоторые данные. Прохладный. Что мне делать дальше? Когда я впервые начал думать об этих данных, моей первой мыслью было, что они могут быть полезным API для веб-приложений. Я веб-разработчик, поэтому все для меня выглядит как веб-источник. Конечно, зачем мне для этого безсерверные? Я мог бы запустить библиотеку iCal в браузере — единственная проблема, с которой я столкнулся бы, — это CORS — возможно. И я всегда мог скопировать файлы iCal на тот же сервер, что и приложение. Но это действительно ограниченное мышление.

Создав это как бессерверное действие, я открыл его для любого источника событий, в Интернете или нет. Как насчет примера чего-то совершенно не связанного с сетью?

Представьте себе службу поддержки, где каждый день мой процесс получает список сотрудников, ответственных за ИТ-работу на случай чрезвычайных ситуаций. Я мог бы представить процесс, с помощью которого, получив список сотрудников, я мог бы сверить родной штат каждого из них со списком праздников для этого штата и определить, не работает ли этот человек сегодня. Это точно такой же код, но я просто использую его в другом контексте.

И здесь важно помнить об этом. Serverless — это не только упрощение создания приложений NodeJS. Вместо этого я создаю ресурс, который можно использовать в различных ситуациях, как прямых (веб-приложение делает запрос), так и косвенных (процесс, выполняемый по расписанию).

Я уже говорил об этом раньше, но мне кажется, что это не так очевидно.

p.s. Вы можете найти исходный код для этой демонстрации здесь: https://github.com/cfjedimaster/Serverless-Examples/blob/master/ical/get.js

Первоначально опубликовано на www.raymondcamden.com 24 августа 2017 г.