opensourcefellows / amplify Goto Github PK
View Code? Open in Web Editor NEWOpen Source Fellow Sandbox
Home Page: https://amplify-app-production.herokuapp.com/
License: MIT License
Open Source Fellow Sandbox
Home Page: https://amplify-app-production.herokuapp.com/
License: MIT License
Here’s a good basic structure documentation site, but the official specification is pretty easy to follow.
The root file must contain a few objects:
We probably want to have as many re-usable parts as possible. We should define a list of them, but there are definitely a few that leap to mind:
Schemas - contain the reusable ones in one place, like Address, every API probably needs the user’s address, or username.
Responses - generic “success” or “404” or other messages we don’t want to write out 5000 times
Error - something went wrong and we don’t know what. Used when other, specifically written error messages do not apply. (case-else)
What to we need to know if we need to define a security scheme (authentication/authorization), or if we’re even using any - that might be handled completely elsewhere, like by GitHub itself?
Documentation should cover any HTTP error responses that are likely to happen and include instructions on how to remedy it and the “200 success” code.
The APIs we are using, not writing, may have their own OpenAPI files already available, so we can potentially copy their stuff into our stuff rather than write it all up from scratch. Not sure what the plan is to keep all that up to date, though. If they change their stuff that we rely on, how do we know?
This template doesn’t include complete coverage of all possible OpenAPI fields, but it’s useful as starter code. In most cases you’ll want to add your own response schemas and reusable components. You can dig into the OAS specification itself or see our OpenAPI and Swagger examples below.
openapi: "3.0.0"
info:
version: 1.0.0 // Version of your API
title: Your API Title
description: An API description template
contact:
name: Your Name
email: [email protected]
url: http://example.com
license:
name: License Name
url: http://example.com/license-url
servers:
- url: http://api.example.com
paths:
/your_endpoint:
get:
description: |
Multi-line description
of your API endpoint
operationId: yourOperation
parameters:
- name: your_param
in: query
description: Description of your param
required: false
schema:
type: string
responses:
'200':
description: Your success description
Use Case: this will allow us to create tests for frontend instead of just backend
Originally posted by @manishapriya94 in https://github.com/ProgramEquity/amplify-back-end/discussions/170#discussioncomment-2099854
End Goal:
Context:
.github/workflows within front-end repo
We are currently using the Google Civic API to look up relevant state and federal representative information based on the user's address. This works very well! 🎉
However, the Civic API is also missing quite a bit of information about those representatives, often including their photos and sometimes even information contact information. 😭
One strategy that we could employ to workaround this shortcoming is to stitch the information provided by the Google Civic API with information provided by other public APIs, such as the Open States database: https://docs.openstates.org/api-v3/ 🔀
I'm unsure of its completeness (it seems pretty thorough at first glance) but it's definitely better than relying on just the incomplete information from the Google Civic API. 😓
References:
When selecting Campaign 3
, entering a zip code (I used 27517
), and selecting a representative, the application calls out to https://murmuring-headland-63935.herokuapp.com/api/lob/templates/tmpl_1057bb3e23kwkkq
which yields a 400
error code. The outcome is that the screen shows two single quotes where the name and corresponding address for the selected representative should be displayed.
This is the Campaign
screen right after you submit a zip
code. See the attached screenshot below.
Looks like src/components/RepresentativeCard.vue
but I'm not 100%.
What is the change proposed? (add a figma screenshot, follow the workflow here)
Steps to reproduce:
ClimateCare
campaign card (this should be Campaign ID 3
) and click the VIEW CAMPAIGN
buttonSubmit
buttonActual behaviour:
The application calls out to https://murmuring-headland-63935.herokuapp.com/api/lob/templates/tmpl_1057bb3e23kwkkq
which yields a 400
error code. The outcome is that the screen shows two single quotes where the name and corresponding address for the selected representative should be displayed.
Expected behaviour:
The name and corresponding address for the selected representative should be displayed immediately underneath the date.
Which topic does this educate the constituent around? (add a short description on how its clearer than the original) ?
Advocacy values to consider:
What are frontend tasks? (if theres any tasks needed outside of the template below, pick a different color like blue)
List files that need to be changed next to task
CC: @frontend-team member, @frontend-coordinator, @research-coordinator
For Coordinator
Part of #128
If a user subscribes to updates, This API will help us collect phone numbers, verify them, before sending updates on the action a person took
Development would take place in twilio.js with set up of functions for each API use twilio provides
Resources:
Context:
User Story
(add pull request to completed checkbox)
Accordion panel 1: Review Letter
Accordion panel 2:User Input
This workflow starts your API and fuzzes it with ForAllSecure Mayhem for API
to find reliability, performance and security issues before they reach
production.
Create a Mayhem for API account at
https://mayhem4api.forallsecure.com/signup
Create a service account token mapi organization service-account create <org-name> <service-account-name>
Add the service account token as a secret in GitHub called "MAPI_TOKEN"
Update the "Start your API" step to run your API in the background before
starting the Mayhem for API scan, and update the api-url
& api-spec
field.
If you have any questions, please contact us at [email protected]
name: "Mayhem for API"
on:
push:
branches: [ main ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ main ]
jobs:
mayhem-for-api:
name: Mayhem for API
# Mayhem for API runs on linux, mac and windows
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
steps:
- uses: actions/checkout@v2
# Run your API in the background. Ideally, the API would run in debug
# mode & send stacktraces back on "500 Internal Server Error" responses
# (don't do this in production though!)
- name: Start your API
run: ./run_your_api.sh & # <- ✏️ update this
- name: Mayhem for API
uses: ForAllSecure/mapi-action@193b709971cc377675e33284aecbf9229853e010
continue-on-error: true
with:
mapi-token: ${{ secrets.MAPI_TOKEN }}
api-url: http://localhost:8080 # <- ✏️ update this
api-spec: http://localhost:8080/openapi.json # <- ✏️ update this
duration: 60
sarif-report: mapi.sarif
- name: Upload SARIF file
uses: github/codeql-action/upload-sarif@v1
with:
sarif_file: mapi.sarif
My experiences with Sequelize have all become cautionary tales, so I guided us away from using it at this point and instead favored just using Knex, which is a more basic "SQL query builder".
However, for maintainability's sake, it is probably worthwhile to consider an ORM for the long-term, especially with some contributors not being too familiar with databases.
I would suggest we investigate Prisma as a potential ORM in particular. 🔺
I think it has a lot of nice features that make it a more solid choice than Sequelize. It should also be able to completely replace our Knex usage, as far as I can tell at this point.
npm install --save objection
require('objection').Model.knex(knexClient)
constituents
table (:warning: currently unused)sent_letters
table (:warning: currently unused)variants
table (:warning: currently unused)objection-orm
that installs the objection
module and sets up the project for creating models.server/db/models/
directory (only exists in the objection-orm
branch)letter-version.js
LetterVersion
Example, at filename server/db/models/letter-version.js
:
const Model = require('./_base')
class LetterVersion extends Model {
static get tableName() {
return 'letter_versions'
}
// ...additional methods, relationships, etc.
}
module.exports = LetterVersion
User stories:
QA
Files: In checkout.js for functions and checkout.test.js for tests
When using the Payment Intents API with Stripe’s client libraries and SDKs, ensure that:
Charges API
When using the Charges API with Stripe’s client libraries and SDKs, ensure that:
Server-side code
In your server-side code, ensure that:
We've decided to use the prebuilt checkout flow instead of the custom payment flow.
In order to this, we'll be using Vue-Stripe Checkout. This requires session creation in the back-end using the steps outlined in Vue Stripe documentation above and deleting the current checkout.js logic.
We also need to ensure createLetter in lob.js only works after a successful payment as seen below. Unsure if anything needs to be changed here if we're doing the prebuilt checkout flow as compared to the custom flow.
router.post('/createLetter', async (req, res) => {
// Get description, to, and template_id from request body
const { description, to, from, template_id, charge } = req.body || {}
const lobApiKey = getLobApiKey()
const lob = new Lob({ apiKey: lobApiKey })
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY)
try {
// Create Lob address using variables passed into route via post body
const letter = await lob.letters.create({
description: description,
to: {
name: to.name,
address_line1: to.line1,
address_line2: to.line2,
address_city: to.city,
address_state: to.state,
address_zip: to.zip
},
from: from.address_id,
file: template_id,
color: false
})
res
.status(200)
.send({ expected_delivery_date: letter.expected_delivery_date })
} catch (error) {
// We'll need a stripe test env key to test this in our integration tests
const refund = await stripe.refunds.create({
charge: charge
})
// TODO handle error for refund error. Not doing this currently because chance of
// user making it this far in the process and both LOB API and Stripe failing is very small.
res.status(500).send({
error: `Something failed! A refund of ${refund.amount} ${refund.currency} has been issued`
})
}
})
⬇️ Table of Contents:
=- Our goal is to enable students towards enterprise architecture while making their onboarding painless
Today we use Actions to perform administrative automations, CI/CD, and use it for CodeQL (in pink)
devcontainer.json
fileResources:
This is a header for multiple screens: 2-8
What is the change proposed? (add a figma screenshot, follow the workflow here)
Which topic does this educate the constituent around? (add a short description on how its clearer than the original) ?
Advocacy values to consider:
What are frontend tasks? (if theres any tasks needed outside of the template below, pick a different color like blue)
List files that need to be changed next to task
CC: @frontend-team member, @frontend-coordinator, @research-coordinator
For Coordinator
What screen is this?
The text on the final action button at the bottom
What is the change propoosed? (add a figma screenshot, follow the workflow here)
Change words to simplify language but still convey you are doing 2 actions (payment via stripe and sending post)
Which topic does this educate the constituent around? (add a short description on how its clearer than the original) ?
Advocacy values to consider:
What are frontend tasks? (if theres any tasks needed outside of the template below, pick a different color like blue)
List files that need to be changed next to task
CC: @frontend-team member, @frontend-coordinator, @research-coordinator
For Coordinator
This is part of the features of the Letter Payment Epic OpenSourceFellows/amplify-front-end#45. The user is informed about the minimum cost of a letter and then offered donation options that can be checked out.
Describing our APIs using OpenAPI is great, but if we want the description to remain complete and accurate, we need to test it! The good news is that if you’re using the api test helper, you get that testing for free.
Take this test, for example:
data = api :post, "/repositories/#{@repo.id}/check-runs", {},
input: {
name: "coverage-stuff",
head_sha: @sha,
status: "in_progress",
started_at: Time.now.utc,
}
assert_equal 200, last_response.status, last_response.body
The api method makes a call to our API and returns the data returned by the resource. By default every api test helper call goes through an OpenAPI validation middleware. A router takes the path you provide, matches the operation, and uses that operation to validate everything about that request (query parameters, response schemas, and more).
If a mismatch is detected by the middleware, it will raise, making your test fail with an helpful error. Imagine, for example, you added a new property called my_new_property to a serializer but forgot to add it to the OpenAPI description. If you have a test returning that new property, it would fail with an error like the following:
2021-03-09 00:00:00 - OpenApi::Validation::Error
Property
my_new_property
is not described by the response200
for operationmy/operation
. This either means you forgot to describe that property in the operation description located atapp/description/operations/my/operation.yml
or that the test is wrong.
More info: https://thehub.github.com/engineering/development-and-ops/public-apis/openapi/contract-testing
There are plenty of validations like this one running when you use the api test method. Thankfully, each error should have enough information on how to fix it. If it doesn’t, open an issue or let us know in #ecosystem-api. Over time this means the OpenAPI description always reflects what happens at runtime, which means our documentation, SDKs, and any tooling that depends on OpenAPI will remain accurate!
##Legacy Behavior
Sometimes tests will fail due to a behavior that’s not described in OpenAPI but used in the test. Generally the failure indicates that we should document it in OpenAPI, but in some cases we’d rather not expose it externally. For example, some obscure and deprecated use case may fail a contract test, but we’d rather not expose it to our documentation and SDKs through the OpenAPI description.
In these cases we still want to describe that behavior in OpenAPI, but make sure it is not exposed anywhere else than in our own internal description. To do so, you can use the x-internal-only OpenAPI extension:
parameters:
On certain occasions you may find it necessary to skip OpenAPI contract testing
It’s possible you may want to test an endpoint and disable the OpenAPI validation middleware. Sometimes a test is sending a wrong value, but you want to test the behavior of the endpoint, disregarding the extra OpenAPI contract testing layer. To do so, you can use skip_openapi_validation when calling the api helper:
data = api :get, "/users",
my_param: "not_supported",
skip_openapi_validation: "Skipping because we want to test the 422 returned when passing an unsupported param"
Additionally to the “per-test” OpenAPI skipping, It’s also possible to opt out an entire path from OpenAPI contract testing. Although we strive to have a 100% accurate description, sometimes internal or deprecated endpoints need to be ignored.
Add a regex for the paths you want to exclude from this OpenAPI validation in our IgnoreList.
User Story:
Tasks:
File to change: SearchReps line 17
This is a part of OpenSourceFellows/amplify-front-end#44 where after user inputs address, we use the Lob API address_verification method. The goal is to create address verification call on user input.
To do:
submit () {
this.formHasErrors = false
Object.keys(this.form).forEach(f => {
if (!this.form[f]) this.formHasErrors = true
this.$refs[f].validate(true)
})
axios.post('https://murmuring-headland-63935.herokuapp.com/api/lob/addressVerification', this.form)
.then((response) => {
console.log(response)
this.message = 'Address verified!'
}
})
.catch(function (error) {
console.log(error)
})
}
to
variable in createLetterOpen tasks
Use Case:
Screen 1: Info on the bill and letter sending
Chat conversation starters:
Screen 2: Info on choosing reps
Screen 3: FAQ around action inputs
User Story:
POST
to back-end to make a call to LOB APIPOST
to back-end to save record into the DBDevelopment Task
Send User Letter to Lob
POST
Function in /api/lob endpoint (lob.js)POST
request - Letters API Save User Letter Data
/api/userletter
endpoint/api/userletter
endpointGET
function /api/userletter
Create an Objection Model for the transactions
table
server/routes/api/checkout.js
Towards #32
User Story:
A user can choose to opt into sms alerts around the cause they wrote a letter for. The twilio integration allows us to follow up with user education via SMS.
Data Structure | Data Report
Feature Discussion
Twilio API Docs
User Journey Miro
Create an Objection Model for the campaigns
table
server/routes/api/campaigns.js
Towards #32
To ease the transitions across the 3 steps before sending letter, we want to use the 'enter command' to signify moving to the next step and implement locking for the accordion steps.
Advocacy groups singly pointed out that customized letters help gather pivotal data points for impactful storytelling thats specific to localized solutions.
Create an Objection Model for the letter_versions
table
server/routes/api/letter_versions.js
Towards #32
Alternative to, or stepping stone towards:
Currently, the frontend portion of our app contained hardcoded URL references to the current production instance of our backend portion's Heroku App, e.g.:
This is highly restrictive to our continued development as the frontend is always referring to the production backend code, thus not allowing us to develop both the frontend and backend portions of a new feature within a single PR.
This also introduces risk when we discuss having dedicated deployments that will not be updated for special launch events such as:
If we cannot address this with #83 in the short term, one workaround we can use is to use the Heroku static buildpack's proxy backends feature.
To implement that would require a few steps.
Find all instances of https://murmuring-headland-63935.herokuapp.com/api/
in the frontend code and replace them with just /api/
.
static.json
Using a Heroku config var reference like API_ORIGIN
:
{
"proxies": {
"/api/": {
"origin": "${API_ORIGIN}/api/"
}
}
}
API_ORIGIN=https://murmuring-headland-63935.herokuapp.com
Now that everything is configured, we need to ensure our latest code with these changes is deployed to the staging frontend Heroku App! 🚀
After confirming that this works as expected with the staging Heroku App:
This will allow us to create a staging environment for the backend code as well, and even a separate PostgreSQL database (probably a good idea to avoid messing up production data) -- if desired! The obvious gotcha to watch out for here would be the Heroku billing costs. 💰
Just remember to update the frontend config vars to point at your preferred backend URLs per environment.
Coupon codes make it more accessible for people of all backgrounds to be able to participate in civic engagement. In our use case, an advocacy organization can give codes at their discretion via email, social media, or through in person.
Within checkout.vue we have a space for a promo code. The use case here if that if a person enters a code in the input text box below and if its under the threshold of times discounted:
We might want to also consider adding messaging if a limit has been reached or if the coupon code doesn't work
Stripe currently lays out the following for integrating coupons into checkout:
It seems you can't apply a coupon directly to the redirectToCheckout method, you have to create a Checkout session first and then use that.
Add a discount to Checkout the sample code is calling Checkout.session.create, not Checkout directly
Quick note
redirectToCheckout Reference Docs doesn't mention a discount or coupon parameter at all, and the sample code again is creating a Checkout session using fetch (to one's own server backend, which is where my expertise ceases and where I'm stifled), to generate a session ID and then passing that into the result where redirectToCheckout is called.
Questions:
Can coupons be done on just client side?
Does coupon exhaust the customizations we have access to?
Our current git precommit hook only checks if our code linting and/or formatting has errors, but does not actually fix such errors when possible.
This is something that is definitely achievable.
Draft PR from the old backend repo: OpenSourceFellows/amplify-back-end#145
We recently combined the addressVerification and createAddress as one api call. However, the new createAddress call does not seem to be working. I would expect createAddress api in lob.js to send me an address_id as a response if I send an address to it. @teakopp We were working on this in the hackathon but don't think we found the problem.
I keep receiving a "Something failed" error instead of an address_id. I have tried different variations of the address and ensured that the address is correct through addressVerification.
addressVerification working - shows its not an issue with the address
@manishapriya94 recently enabled Heroku Review Apps (TL;DR: a separate deployed Heroku App per PR).
However, since it looks like the Apps cannot be configured [much] in the Heroku UI, I'm guessing that this repo will need to add an app.json
manifest file to specify some critical pieces like which Heroku buildpacks need to be used.
e.g.
{
"buildpacks": [
{
"url": "heroku/nodejs"
},
{
"url": "https://github.com/heroku/heroku-buildpack-static"
}
]
}
This issue describes the various steps needed to make and deploy changes to our OpenAPI description.
Modify
First, make your modifications to the OpenAPI description files.
Make sure your operations are assigned to their associated endpoint implementations.
Regenerate
Because not all operations are found in all releases, we have an additional build step that builds the OpenAPI roots (mainly paths and other top level metadata) based on x-github-releases and related release-specific changes
The OpenAPI root files are found at the root of app/api/description folder.
app/api/description/
├── api.github.com.yaml
├── ghes-2.18.yaml
├── ghes-2.19.yaml
├── ghes-2.20.yaml
├── ghes-2.21.yaml
├── github.ae.yaml
To update the root files, run:
$ ./bin/openapi generate-root-files
✅ Wrote 70148 bytes to app/api/description/ghes-3.0.yaml
✅ Wrote 71065 bytes to app/api/description/ghes-3.1.yaml
✅ Wrote 66196 bytes to app/api/description/ghes-2.22.yaml
✅ Wrote 50868 bytes to app/api/description/ghes-2.18.yaml
✅ Wrote 51514 bytes to app/api/description/ghes-2.19.yaml
✅ Wrote 51860 bytes to app/api/description/ghes-2.20.yaml
✅ Wrote 56220 bytes to app/api/description/ghes-2.21.yaml
✅ Wrote 78869 bytes to app/api/description/api.github.com.yaml
✅ Wrote 63453 bytes to app/api/description/github.ae.yaml
🎉 Root files updated! Please commit the resulting files.
Preview
To preview your changes on https://docs.github.com/, see Publishing REST API docs or reach out to #docs-apis-and-events for help.
Deploy
Once you’ve made your changes, you’ll have to deploy github/github and merge the default branch before the changes get reflected to docs and our open source descriptions. Thankfully, this amazing guide explains the process in detail. Feel free to reach out to #ecosystem-api for help.
Sync
Once github/github has been deployed and builds run on the default branch, a pull request will be automatically opened on github/rest-api-description and github/docs-internal by our friendly helper github-openapi-bot.
The github/rest-api-description PR must be merged to keep the open source OpenAPI description up to date.
The github/docs-internal PR must be merged to keep https://docs.github.com/ up-to-date.
For now, while we gain confidence, the PRs need to be merged manually by a human. Eventually, we hope to make this process will be fully automatic.
Mission statement: Users can read the “raw” OpenAPI docs since they’re written in YAML, but for a better experience, we want to build a Pages site that intakes the raw files and outputs formatted, easy-to-read information.
Context
We currently document our API with manually updating Wiki pages, which are prone to inaccuracy and to becoming outdated. 😕
One alternative to consider would be documenting the API with a central OpenAPI schema file.
Side benefit: if desired, we can also then use additional Express middleware tooling for request validation. I have mixed feelings about this in practice based on past experiences but it is not unlikely that there are better tools available these days than a few years back. 🤷🏻♂️
With either of those approaches, we can then generate the documentation for it using the existing OpenAPI/Swagger tooling (see example demo page).
SMART exit criteria:
gh-pages
branchgh-pages
branchUser Story:
implementation:
check_payment: Boolean for Stripe Succesful payment:
create_letter function: Post Lob Letter API Call:
Context:
CI ensures all tests are built and executing when a pull request is opened:
What is CI/CD?
In the context of DevSecOps its the continuous integration and continuous deployment with each commit
Context of current CI/CD
I've noticed that our ESLint and Prettier configuration don't seem to be including and/or achieving what I would've expected with our *.vue
files. Guessing we are missing some Vue-specific configuration and/or plugins there. 🔍
Our current git precommit hook only checks if our code linting and/or formatting has errors, but does not actually fix such errors when possible.
This is something that is definitely achievable.
Draft PR from the old backend repo: OpenSourceFellows/amplify-back-end#145
Probably helpful: https://www.digitalocean.com/community/tutorials/vuejs-vue-eslint-prettier
Given our application's nature, we should ideally just host the frontend + backend code with one Express port (for both dev and production). This should simplify our onboarding for local development, our Codespaces configuration, and our deployment strategy.
In terms of deployment, we would only need a single self-contained Heroku App, and could even consider using other hosting services like Azure, AWS, GCP, etc. 🚀
While researching this, I found that it is easy enough to have an Express app host a built production Vue app but I struggled to find good resources on how to do it in a way that still supported hot-reloading of Vue from a development perspective.
The most interesting resource I found on the topic was this one, which seemed very promising in its unique approach of mounting the Express app into the Vue app (vue-cli-service
app, that is) since it already has an internal Express server. 🔄
Related:
We should add the Volar Vue.volar
Vue VSCode extension to Codespaces extension list to make it easier to work with Vue from a Codespace.
References:
This is one of the tasks of Configuring Twilio for SMS
TWILIO_SECRET_KEY needs to be configured in routes within
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.