Как я завершил свой собственный проект кодирования

Презентация

В этой статье будет рассказано, как я реализовал нод-проект, чтобы получить обычный аккаунт для своего кошелька криптовалют.

Реализация такого небольшого и личного проекта важна для того, чтобы стать лучшим разработчиком. Это повод изучить новые технологии, попробовать новые методы и протестировать фреймворки без последствий. Создание программы — это знание, которое вы можете получить только на практике, но в вашей профессиональной карьере у вас действительно нет возможности развиваться. Личные проекты здесь, чтобы заполнить пробел.

В моих собственных случаях у меня есть некоторый опыт работы с javascript, и я уже сделал несколько проектов node. Я хотел прогрессировать с этим инструментом, и это было моей главной мотивацией, помимо чистой полезности знания того, сколько стоит мой кошелек.

Сама статья не столько о технологиях. Я предоставлю ссылки на технологическую статью, которую я прочитал, чтобы выполнить свою программу, и я уверен, что вы найдете хорошие статьи и на своей стороне. Нет, эта статья о том, как я это делал, какие были трудности и как я их решал. Здесь нет объяснения использования фреймворка. Его цель — проиллюстрировать, что происходит, когда вы действительно пытаетесь что-то сделать со своими знаниями, как найти решение проблем, возникающих в реальных случаях, а не в идеальном учебнике. Короче говоря, это о реальной жизни разработчика.

Весь мой код был опубликован на github. Вы можете найти его здесь: https://github.com/FChenebit/CryptoWallet

Предварительные работы и испытания

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

Резюме этого проекта таково: «Я хочу, чтобы программа работала без моего ведома, чтобы раз в день присылать мне краткую информацию о моем кошельке, сумме его значений и деталях для каждой валюты».

Обычно вторым шагом будет подробное описание этого резюме. Это будет этап проектирования (см. следующую главу). Но так как это проект, который я также использовал для обучения; Я знал, что мне придется освоить некоторые моменты, которые я еще не освоил. И поскольку я хотел закончить этот проект, я не хотел, чтобы меня слишком долго блокировали чем-то, что я не смог бы реализовать так, как хотел. Мое невежество здесь было слепым пятном: возможно, я пытался сделать что-то невозможное или очень сложное, не зная об этом. Итак, я должен сначала лучше понять вещи, которые я хотел изучить, и я бы сделал некоторое POC (доказательство концепции), чтобы быть уверенным, что смогу завершить свой проект.

  • Облако: если моя программа должна работать одна, она должна быть на облачном сервере. Я прочитал несколько статей о Heroku и попробовал. Я вижу, что это платформа PaaS, а не IaaS, поэтому у меня будет меньше свободы, чем на собственном сервере, но многое уже развернуто. Я просмотрел документацию. Моя главная проблема заключалась в том, что Heroku предполагал, что я сделаю веб-сайт или веб-приложение, которое будет сервером. Чтение документации по началу работы многому меня научило, но не помогло в моем случае. Мне нужен был способ запустить мою программу узла в заданное время. Это не стандартная функция, но у Heroku есть надстройка, способная сделать это: Планировщик Heroku. Я нашел его, выполнив поиск по расписанию в документации Heroku, так как хотел запланировать задачу. Это дополнение к документации также указало мне на один динамометр, который он будет использовать, и на то, как запустить его в консоли Heroku, чтобы протестировать его, не дожидаясь времени расписания. Чтобы проверить, как это работает, я сделал базовую программу node, которая будет регистрировать сообщение hello world и видеть, как оно запускается в консоли и в запланированное время. Здесь мне помог мой опыт: я знал, что должен быть способ планировать некоторые задачи, так как это очень распространенная потребность. Я, возможно, пытался разработать свой собственный планировщик, но часто для общей проблемы есть общее решение, и ваше не победит его. Это будет не так проверено, у вас не будет столько опыта, а многие пользователи уже полюбили все возможные баги. Поверьте мне в этом, не изобретайте велосипед.
  • Линт: Я хотел, чтобы мой код следовал общепринятым рекомендациям, повышал удобочитаемость и предотвращал некоторые ошибки. Поскольку я использую Visual Studio, es lint кажется хорошим выбором. Я искал эти ключевые слова: visual studio code eslint node.js, и первый ответ выглядит нормально: https://www.digitalocean.com/community/tutorials/linting-and-formatting-with-eslint-in. -vs-код». После использования заработало.
  • Обещания: при использовании JavaScript я часто попадал в ад обратных вызовов: необходимость передавать обратный вызов в качестве параметра и трудности с его использованием. Модуль Async здорово помог, но мне показалось, что этого недостаточно. Я слышал о обещаниях и решил использовать их в этом проекте. Я нашел этот сайт во время повседневного поиска: https://github.com/goldbergyoni/nodebestpractices, и там говорилось о промисах и async/await, но это было не очень понятно. Итак, я искал по этим ключевым словам: асинхронное ожидание узла 8 (поскольку асинхронность и ожидание — это функции узла 8). Я прочитал последнюю, так как эта функция появилась совсем недавно. Это интернет-ловушка: поскольку переработки не так много, вы можете найти много устаревшей информации. Это еще более важно для Node, так как это фреймворк, который развивается довольно быстро. Чтобы проверить свое обучение, мне понадобился асинхронный модуль.
  • API: Мне нужен был API, чтобы узнать текущий курс криптовалюты. Я также буду использовать его для тестирования асинхронности и ожидания. Веб-сайт, который я использовал для покупки своей криптовалюты, не имеет такого API, поэтому я искал: API для получения COS/BTC, поскольку COS была одной из валют, которые у меня были. Я посмотрел на некоторые результаты и выбрал https://coinmarketcap.com/. Для тестирования различных API я использовал инструмент postman. Очень полезно отправлять запросы, чтобы понять, как работает REST API. Coinmarket прост в использовании и предоставляет мне все необходимые данные. Я сделал тест с async/await, следуя примеру на сайте выше, и он работал нормально. Мне нужно было выбрать модуль для выполнения запроса. В моем предыдущем уроке я использовал запрос, но этот модуль устарел. В проекте запроса предлагается несколько альтернатив. Got казался более простым в использовании, и мой POC подтвердил это. Таким образом, не было никакой необходимости искать лучшее решение, так как у меня было хорошее решение, которое подойдет.

  • Почта: самая простая часть POC. Быстрый поиск показывает мне модуль nodemailer. Вроде получил, со своей задачей справляется отлично.

Со всеми этими предварительными испытаниями я был очень уверен в своей способности закончить этот проект, так как большинство технических трудностей уже решено. Вероятно, мне придется настроить базу данных, но это очень распространенная проблема, поэтому я уверен, что найду решение. Теперь мне «просто» нужно спроектировать именно то, что должно быть сделано, и организовать код для реализации этого дизайна.

Дизайн

В профессиональном проекте, когда вы создаете приложение для кого-то, вы сначала описываете ему, что приложение будет делать, чтобы иметь соглашение. Затем вы подробно описываете, как будет сделано приложение (какой язык, какой компонент…), затем делаете и тестируете его. Первые 2 шага обычно максимально детализированы. Это может быть проблемой, так как трудно точно знать и описать, что вы делаете. Если вы хотите построить дом, у вас могут быть планы архитектора, но они будут сильно отличаться от настоящего дома. А описать дом гораздо проще, чем описать программу. Более того, описывая, чего хочет клиент, вы быстро доберетесь до технических деталей, о которых он не хочет слышать (клиент/сервер, вам нужен API…). Этого быть не должно, но в целом техника влияет на спецификацию. Вот почему сейчас популярны agile-методы: они требуют только описания короткой части программы, чтобы согласовать ее с пользователем, прежде чем продолжить.

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

Итак, учитывая все это, вот мой дизайн криптокошелька:

Цель

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

NB: сначала я думал, что отправляю обычную электронную почту раз в неделю, но с планировщиком Heroku это было бы сложно, а раз в день это не является таким ограничением. В личном проекте вы можете адаптировать то, что хотите, к техническим ограничениям.

Функциональность

С этой целью я вывожу следующий список функций внутри программы в произвольном порядке:

  • Получите актуальную цену криптовалюты с помощью одного или нескольких API.
  • Подсчитайте общую стоимость моего кошелька.
  • Храните данные моего кошелька.
  • Прочитайте предыдущее значение моего кошелька: за неделю до, за 1 месяц до, за 6 месяцев до, за год до.
  • Проверьте, превышаю ли я порог.
  • Прочитайте в конфиге количество валюты, которой я владею.
  • Прочитайте порог в конфиге. (иметь жесткую константу в коде обычно плохо).
  • Написать оповещение по электронной почте.
  • Вычислите каждую строку в электронной почте с процентом эволюции.
  • Пишите обычное электронное письмо.
  • Послать электронное письмо.
  • Возможность отправить ошибку.

Ошибка отправки является жизненно важным компонентом. Вам нужно, чтобы ваша программа могла сообщить вам, что он делает, потому что иногда это может быть единственным способом отладки.

Компоненты

По функционалу могу перечислить все компоненты программы:

  • Logger: откройте функцию журнала, которая будет тестировать среду. Если среда разработки (т.е. на моем собственном компьютере), то пишите в лог консоли. Если производство (т.е. на Heroku), то отправить письмо.
  • Почта: выставьте sendMail, который отправит электронное письмо с информацией, полученной в конфигурации. Его параметрами являются тема и текст письма.
  • Планировщик: закажет все остальные вызовы и будет вызван Heroku.
  • Запрос: получить данные из API, протестированного в POC.
  • База данных: будет получать данные истории из базы данных. Будьте осторожны, у меня может не быть какой-либо даты (например, я должен использовать программу один год, чтобы иметь годовую историю).
  • Создать: написать обычное письмо и электронное письмо с предупреждением.

Первые 2 модуля для разработки — это Mail и Logger. Как было сказано выше, очень важно иметь возможность протоколировать сообщения, чтобы понять, что происходит в вашей программе, поэтому регистратор всегда должен быть вашим первым модулем. Именно поэтому hello world является обычным первым примером на любом языке: чтобы узнать, что наша программа запущена, вам нужен вывод.

Далее есть 2 варианта: следовать порядку обработки (запрос, база данных, затем компоновка или сверху вниз), или сделать это в обратном порядке (или сверху вниз). С верхом вниз вы можете ошибиться, угадывая интерфейс еще не выполненной функции, с верхом вниз вы можете делать вещи, которые вы не будете использовать, и потерять глобальное видение программного обеспечения. В таком случае и дел не так много, и снижается риск наделать бесполезных вещей. Также у меня не так много свободы в данных и так в параметрах. Так что вниз сверху будет мой выбор.

Итак, окончательный порядок: Почта -> Регистратор -> Запрос -> База данных -> Составление -> Планировщик.

Настройка моей среды

После проектирования мне нужно было настроить инструменты, которые я буду использовать для разработки крипто-предупреждения.

  • IDE: я буду использовать Visual Studio. Его настройка достаточно проста. Единственным моментом было использование ES-Lint, но я объяснил это выше.
  • Контроль версий: инструмент контроля версий совершенно необходим, когда над одним проектом работает много людей. Если вы один, это может показаться менее полезным. Со своей стороны, я вижу 2 интереса в системе управления версиями: она сохраняет ваш код на сервере, и поэтому вы никогда его не потеряете. Вы также можете легко работать на другом компьютере. Другой момент — история: если вдруг в вашем коде возникла проблема, вы можете проверить, как он был изменен, чтобы помочь вам понять, что произошло. Мне нужно 2 репозитория: один для Heroku, потому что это способ доставки кода на их платформу, и один для github в качестве поддержки этой статьи. Я действительно не знаю, как это сделать, поэтому я продолжаю пробовать. Документ Heroku предлагает создать приложение Heroku в существующей папке, и Heroku добавит эту папку в репозиторий, которым он управляет. Я попробовал это в папке, в которой уже есть репозиторий, и все сработало нормально. SourceTree показывает мне 2 удаленных репозитория. Я попробовал это перед тем, как начать кодировать, на случай, если я все потеряю. Забавный факт, у меня была проблема с SourceTree, которая, похоже, связана с аутентификацией. Сообщение было неясным. (очень важно, чтобы все сообщения были четкими и давали всю доступную информацию. Читатель разберет то, что ему нужно, не пытайтесь сделать это за него). Он работал в консольном режиме. После некоторой попытки я закрыл окно проекта в SourceTree, а затем увидел сообщение об ошибке, скрытое этим окном. Это сообщение просило меня выполнить какое-то действие на github, и после этого оно сработало. Иногда модальные окна на Mac выглядят беспорядочно.

  • Время: я решила работать по часу каждый вечер, когда мой ребенок спит. Здесь очень важно быть регулярным, чтобы не потерять мотивацию.

Когда все готово, пришло время кодировать.

Реализация

почтовая программа

В POC, который я сделал, единственным недостающим пунктом было обещание. В документации nodemailer объясняется, что он изначально поддерживает промисы. Мне просто нужно опустить параметр обратного вызова, и функция sendMail вернет обещание.

Так что моя обертка будет добавлять только параметры, которые никогда не изменятся: хост и порт SMTP, пользователь и пароль, адрес от и до. Эти параметры будут введены в конфигурацию. На Heroku эта конфигурация будет установлена ​​в config vars в настройках. Чтобы протестировать локально, я поместил ту же переменную конфигурации в сценарий оболочки, который затем запустит мою программу node. Конечно, этот сценарий оболочки не был зафиксирован. Вот как это выглядит:

Я протестировал его локально и на Heroku с тестом Ok (для отправки электронной почты) и тестом на ошибку (с отсутствующими переменными конфигурации). Я не буду писать об этом в следующих главах, но я сделал такой тест для всех модулей.

Соответствующие коммиты: 0ede84f и 3bdfa6d.

Регистратор

Этот модуль тоже очень простой. Я тестирую переменную конфигурации, чтобы узнать, использую ли я локальную или Heroku (значение DEV или PRODUCTION).

Соответствующие коммиты — 9c1a9fb и 1af355b.

Запрос

Это был первый модуль, который был немного сложнее, но большая часть работы была сделана в POC. Я использовал обещание. Я просто обернул вокруг него просьбу, которую я сделаю. Я использовал константу для URL-адреса вместо переменной конфигурации, так как никогда не буду ее менять. Я боялся проблем с управлением ошибками, но магия асинхронности отлично с этим справилась. Я использовал https://jsonlint.com/ для анализа результата запроса. Я полагал, что у меня проблема с Heroku, так как результат был в консоли, а не по электронной почте. Но проблема также возникла локально, и я этого не понял (очень легко неправильно понять сообщение журнала и искать не в том месте). Проблема - ошибка LPN105_510 при отправке письма. Интернет не знает эту ошибку. Я предположил, что это антиспамовый фильтр. Я оставил это на потом.

База данных

Мне нужны были данные, чтобы иметь некоторую историю о значениях. Моей первой мыслью было сохранить эти данные в файл, так как объем будет очень маленьким. (помните, мой девиз — делать вещи максимально простыми). К сожалению, Dyno (сервер на Heroku) не сохраняет файл между каждым запуском, поэтому мне нужна база данных. Mongo мог бы быть хорошим выбором, но для Heroku нет бесплатного дополнения для его использования. Поэтому я решил использовать базу данных PostGreSQL. POC мог бы быть хорошей идеей, но в то время я не думал, что мне понадобится база данных.

Документация Heroku по PostGreSQL достаточно понятна. Мне нужно дополнение для Heroku и локальная база данных. У меня будет только одна таблица с именем currency_rate, с кодом валюты, датой курса и значением курса. Я установил pgAdmin, чтобы иметь легкий доступ к своей базе данных, но этот инструмент оказался не очень эффективным. Вместо этого я решил работать в консоли, так как у меня не так много задач: мне нужно было создать таблицу и некоторые тестовые данные. У меня возникла ошибка при создании таблицы, так как я использовал CamelCase для имени таблицы (CurrencyRate). С подчеркиванием работало (currency_rate).

Сначала я попытался заставить работать базовый запрос: select NOW();. Но не получилось, мою программу нужно было убить. (Проблема в коммите 1943387). Я сначала подумал, что это проблема с промисами, но нет. Поэтому я попытался начать и завершить подключение к базе данных. Это тоже не работает, но с новым сообщением об ошибке я вижу, где была моя проблема. Требуется начать и завершить соединение с моей базой данных, но если я экспортирую client.connect() вместо возврата client.connect(), я не возвращаю промис, я выполняю его немедленно. Это исправление сделано в коммите 92470a0.

У меня все еще была проблема с Heroku, но я вижу в документации, что моя конфигурация отличается: для rejectUnauthorized установлено значение true при разработке, а мне нужно установить для него значение false на Heroku. Для этого я использую тернарный оператор. Это необычный оператор, но он может вам очень помочь в таком случае.

Я предпочитаю не использовать экзотические функции, так как они делают код менее понятным, но этот очень полезен, и я делаю для него исключение.

Поскольку сейчас мой запрос работает, я буду использовать свои тестовые примеры. Я установил их в первом коммите для базы данных (1943387). Я старался иметь много разных кейсов, и хотел, чтобы их было легко отличить друг от друга, чтобы не ошибиться, когда увижу их в своих тестах. Поскольку я не уверен, что тариф, который я ищу, доступен на дату, на которую я смотрю, я выберу следующую доступную дату. Позже мы увидим, что это была ошибка.

Кроме того, позаботьтесь о привязке параметров к запросу. Вы можете просто создать строку и добавить их внутрь этой строки, но привязка выполнит некоторые дополнительные проверки, которые предотвратят ошибки преобразования. Эти ошибки особенно случаются с данными типа даты.

Тру Норманд

Прежде чем перейти к модулю композитора, я сделал большой перерыв. На самом деле, я провожу онлайн-тренинг, который был запланирован на долгое время. Этот перерыв останавливает мой проект довольно надолго. Поскольку я знал, что остановлюсь, было бы лучше закончить композитор до базы данных и иметь ежедневную почту без истории. У меня было бы что-то полезное, что я мог бы закончить позже. Здесь я рискнул остановить свой проект и никогда не продолжать его. Этого не произошло, но риск был ошибкой.

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

Композитор

Затем я был готов продолжать основной путь. Моя проблема в том, что Composer не был четко разработан. Я знаю, что он должен был сделать, но я недостаточно думал о том, как это сделать. Но поскольку мои модули независимы, я могу сделать это сейчас.

Я хотел в своей почте строку по валюте, с реальной и всей исторической ценностью. Я могу легко получить фактическое значение по валюте из запроса, но из базы данных у меня есть вся дата из моей истории, а затем каждая валюта. Мне нужно было «перевернуть» этот результат. Итак, мой алгоритм будет выглядеть так:

  1. Получить актуальные данные из запроса
  2. Получить исторические данные из базы данных за одну неделю истории
  3. Получить исторические данные из базы данных за один месяц истории

· …

4. Вычислить общее значение из результата запроса

5. Запишите общий результат в виде строки

6. Включите результат базы данных

7. Для каждой валюты укажите фактическое значение и историческое значение, а затем введите фактическое значение для будущих писем.

8. Отправьте строку, написанную по электронной почте

Поэтому я изменил свой дизайн: мой модуль расписания будет реализовывать вышеуказанный алгоритм. Его новая цель — запланировать выполнение запроса. Вычислительный модуль заменит компоновку и будет вычислять общее фактическое значение, а также получать результаты из базы данных. Модуль планировщика будет отправной точкой моей программы. Я сохранил index.js для тестирования.

Я сначала сделаю все, что связано с реальной стоимостью, а потом займусь историей. Первая задача довольно проста: я помещаю количество криптовалюты, которой владею, в переменную окружения. Вы можете прочитать это в коммите beea66a. Я проверил, что получил сообщение об ошибке, когда одна переменная отсутствует. Это работало локально, но не на Heroku. Я провел некоторый тест с сообщением в журнале. Как и ожидалось, проблема была в этих переменных окружения. Это было ожидаемо, потому что это разница между двумя средами. На самом деле я поставил их не на тот сервер Heroku.

Вторая часть была немного сложнее. Моя первая проблема заключалась в том, что у меня может не быть тарифа на желаемую дату, потому что программа может запускаться не каждый день. Итак, я определил интервал для каждого исторического исследования: 3 дня для одной недели, 7 для одного месяца… Сначала я думал, что буду использовать этот интервал в вычислениях или расписании, но это была плохая идея. Лучший способ - изменить модуль базы данных: вместо одной даты я использую 2 параметра в параметрах и беру первую дату между двумя параметрами в параметрах. При этом я совершенно уверен, что у меня есть исторические данные, если они существуют, даже если не в конкретный день.

Перевернуть базу данных оказалось довольно легко, как только я понял, что нужно делать. У меня есть из базы данных что-то вроде этого: database -1W = [{currency_code,rate_value,rate_date}] ; и я хотел что-то вроде этого: [ {BTC : {6M:Rate,1W:Rate}… Я проверил с помощью отладчика правильность моего понимания возвращаемой структуры (здесь также могло помочь сообщение журнала).

У меня только что возникла проблема в планировщике, так как цикл foreach (currency) вызывает функцию стрелки, которая не является асинхронной, поэтому он не может вызвать обещание базы данных. Я решил это, создав промисы в цикле foreach, а затем вызывая их после цикла.

Все эти модификации находятся в коммите: af8a0ce.

Предупреждающая почта тоже была довольно простой. Я использую запрос и вычисление, чтобы получить фактическое значение, и если это значение превышает пороговое значение, я отправляю простое электронное сообщение с этой информацией. Моей последней задачей было удалить index.js, который больше не использовался.

Вывод

В конце этого проекта я счастлив, что смог его закончить. До финиша дошли 2 очка:

  • Каждая задача была маленьким шагом, с быстрым успехом.
  • У меня была общая дорожная карта, которой я следовал, поэтому я знал, где идти, чтобы закончить.

По сути, в вашем методе работы всего 2 вопроса: что я буду делать дальше, и как я закончу, каков следующий шаг и каков план до конца.

Пока у вас есть эти 2 ответа, вас ничто не остановит.