Blog API example (#377)

This commit is contained in:
Travis Reeder
2017-09-29 15:59:39 -07:00
committed by GitHub
parent a491d1bfde
commit 35bcc4e658
39 changed files with 218 additions and 0 deletions

View File

@@ -0,0 +1,34 @@
# Blog API in 10 minutes
This is a simple blog API with a function to receive a list of posts and a function to create a post.
## Run it
```
# Start MySQL:
docker run --name mysql --net=host -p 3306:3306 -e MYSQL_ROOT_PASSWORD=pass -d mysql:8
docker run -it --rm --link mysql:mysql mysql mysql -ppass -hmysql -e "create database blog"
docker run -it --rm --link mysql:mysql mysql mysql -ppass -hmysql -e "show databases"
# create schema
fn run -e DB_USER=root -e DB_PASS=pass schema
# Test locally:
# Check if any posts, should be none
fn run -e DB_USER=root -e DB_PASS=pass posts
# Add one
cat post.json | fn run -e DB_USER=root -e DB_PASS=pass posts/create
# Check again
fn run -e DB_USER=root -e DB_PASS=pass posts
# Set app configs
fn apps config set blog DB_USER root
fn apps config set blog DB_PASS pass
# fn deploy it!
fn deploy --all
```
## TODO:
* [ ] Add some way to ignore funcs on deploy, ie: schema

View File

@@ -0,0 +1 @@
name: blog

View File

@@ -0,0 +1,22 @@
package main
import (
"encoding/json"
"fmt"
"os"
)
type Person struct {
Name string
}
func main() {
p := &Person{Name: "World"}
json.NewDecoder(os.Stdin).Decode(p)
mapD := map[string]string{
"message": fmt.Sprintf("Hello %s", p.Name),
"posts": "http://localhost:8080/r/blog/posts",
}
mapB, _ := json.Marshal(mapD)
fmt.Println(string(mapB))
}

View File

@@ -0,0 +1,5 @@
version: 0.0.20
runtime: go
entrypoint: ./func
build_image: ""
run_image: ""

View File

@@ -0,0 +1,4 @@
{
"title": "Blog Post 1",
"body": "This is the body. This is the body. This is the body. This is the body. This is the body. This is the body. "
}

View File

@@ -0,0 +1,5 @@
source 'https://rubygems.org'
gem 'json', '> 2'
gem 'sequel'
gem 'mysql2'

View File

@@ -0,0 +1,5 @@
source 'https://rubygems.org'
gem 'json', '> 2'
gem 'sequel'
gem 'mysql2'

View File

@@ -0,0 +1,20 @@
require 'json'
require 'sequel'
DB = Sequel.connect("mysql2://docker.for.mac.localhost/blog?user=#{ENV['DB_USER']}&password=#{ENV['DB_PASS']}")
payload = STDIN.read
if payload == ""
puts ({"error" => "Invalid input"}).to_json
exit 1
end
payload = JSON.parse(payload)
# create a dataset from the items table
items = DB[:posts]
# populate the table
items.insert(:title => payload['title'], :body => payload['body'])
puts ({"status"=>"success", "message" => "Post inserted successfully."}).to_json

View File

@@ -0,0 +1,5 @@
version: 0.0.8
runtime: ruby
entrypoint: ruby func.rb
build_image: treeder/ruby-mysql
run_image: treeder/ruby-mysql

View File

@@ -0,0 +1,26 @@
{
"tests": [
{
"input": {
"body": {
"name": "Johnny"
}
},
"output": {
"body": {
"message": "Hello Johnny"
}
}
},
{
"input": {
"body": ""
},
"output": {
"body": {
"message": "Hello World"
}
}
}
]
}

View File

@@ -0,0 +1,14 @@
require 'json'
require 'sequel'
DB = Sequel.connect("mysql2://docker.for.mac.localhost/blog?user=#{ENV['DB_USER']}&password=#{ENV['DB_PASS']}")
items = DB[:posts]
rlist = []
items.each_with_index do |x,i|
STDERR.puts "item: #{x}"
rlist << x
end
r = {posts: rlist}
puts r.to_json

View File

@@ -0,0 +1,5 @@
version: 0.0.2
runtime: ruby
entrypoint: ruby func.rb
build_image: treeder/ruby-mysql
run_image: treeder/ruby-mysql

View File

@@ -0,0 +1,26 @@
{
"tests": [
{
"input": {
"body": {
"name": "Johnny"
}
},
"output": {
"body": {
"message": "Hello Johnny"
}
}
},
{
"input": {
"body": ""
},
"output": {
"body": {
"message": "Hello World"
}
}
}
]
}

View File

@@ -0,0 +1,5 @@
source 'https://rubygems.org'
gem 'json', '> 2'
gem 'sequel'
gem 'mysql2'

View File

@@ -0,0 +1,10 @@
require "sequel"
DB = Sequel.connect("mysql2://docker.for.mac.localhost/blog?user=#{ENV['DB_USER']}&password=#{ENV['DB_PASS']}")
# create a posts table
DB.create_table :posts do
primary_key :id
String :title
String :body
end

View File

@@ -0,0 +1,5 @@
version: 0.0.2
runtime: ruby
entrypoint: ruby create.rb
build_image: treeder/ruby-mysql
run_image: treeder/ruby-mysql

View File

@@ -0,0 +1,26 @@
{
"tests": [
{
"input": {
"body": {
"name": "Johnny"
}
},
"output": {
"body": {
"message": "Hello Johnny"
}
}
},
{
"input": {
"body": ""
},
"output": {
"body": {
"message": "Hello World"
}
}
}
]
}

View File

@@ -0,0 +1,5 @@
# App Example
This shows you how to organize functions into a full application and deploy them easily with one command.
See [apps documentation](/docs/developers/app.md) for details on how to use this.

View File

@@ -0,0 +1,3 @@
name: helloapp
config:
foo: bar

View File

@@ -0,0 +1,9 @@
puts %{
<div style="margin-top: 20px; border-top: 1px solid gray;">
<div><a href="/r/#{ENV['FN_APP_NAME']}/ruby">Ruby</a></div>
<div><a href="/r/#{ENV['FN_APP_NAME']}/node">Node</a></div>
<div><a href="/r/#{ENV['FN_APP_NAME']}/python">Python</a></div>
</div>
</body>
</html>
}

View File

@@ -0,0 +1,7 @@
name: footer
version: 0.0.13
runtime: ruby
entrypoint: ruby func.rb
headers:
content-type:
- text/html

View File

@@ -0,0 +1,58 @@
package main
import (
"fmt"
"html/template"
"log"
"os"
)
type Link struct {
Text string
Href string
}
func main() {
const tpl = `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{{.Title}}</title>
</head>
<body>
<h1>{{.Title}}</h1>
<p>{{.Body}}</p>
<div>
{{range .Items}}<div><a href="{{.Href}}">{{ .Text }}</a></div>{{else}}<div><strong>no rows</strong></div>{{end}}
</div>
</body>
</html>`
check := func(err error) {
if err != nil {
log.Fatal(err)
}
}
t, err := template.New("webpage").Parse(tpl)
check(err)
appName := os.Getenv("FN_APP_NAME")
data := struct {
Title string
Body string
Items []Link
}{
Title: "My App",
Body: "This is my app. It may not be the best app, but it's my app. And it's multilingual!",
Items: []Link{
Link{"Ruby", fmt.Sprintf("/r/%s/ruby", appName)},
Link{"Node", fmt.Sprintf("/r/%s/node", appName)},
Link{"Python", fmt.Sprintf("/r/%s/python", appName)},
},
}
err = t.Execute(os.Stdout, data)
check(err)
}

View File

@@ -0,0 +1,4 @@
name: app
version: 0.0.70
runtime: go
entrypoint: ./func

View File

@@ -0,0 +1,9 @@
puts %{
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>My App</title>
</head>
<body>
}

View File

@@ -0,0 +1,7 @@
name: header
version: 0.0.11
runtime: ruby
entrypoint: ruby func.rb
headers:
content-type:
- text/html

2
examples/apps/hellos/node/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
node_modules/
Dockerfile

View File

@@ -0,0 +1 @@
# Node function

View File

@@ -0,0 +1,10 @@
fs = require('fs');
name = "do you speak node?";
try {
obj = JSON.parse(fs.readFileSync('/dev/stdin').toString())
if (obj.name != "") {
name = obj.name
}
} catch(e) {}
console.log("Hello, " + name);

View File

@@ -0,0 +1,4 @@
name: node
version: 0.0.13
runtime: node
entrypoint: node func.js

View File

@@ -0,0 +1,7 @@
{
"name": "my-awesome-func",
"version": "1.0.0",
"dependencies": {
"is-positive": "^3.1.0"
}
}

View File

@@ -0,0 +1 @@
packages/

View File

@@ -0,0 +1 @@
# Python function

View File

@@ -0,0 +1,21 @@
import sys
import os
import json
sys.stderr.write("Starting Python Function\n")
name = "I speak Python too"
try:
if not os.isatty(sys.stdin.fileno()):
try:
obj = json.loads(sys.stdin.read())
if obj["name"] != "":
name = obj["name"]
except ValueError:
# ignore it
sys.stderr.write("no input, but that's ok\n")
except:
pass
print "Hello, " + name + "!"

View File

@@ -0,0 +1,4 @@
name: python
version: 0.0.11
runtime: python
entrypoint: python2 func.py

3
examples/apps/hellos/ruby/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
bundle/
.bundle/
Dockerfile

View File

@@ -0,0 +1,3 @@
source 'https://rubygems.org'
gem 'json', '> 1.8.2'

View File

@@ -0,0 +1 @@
# Ruby function

View File

@@ -0,0 +1,25 @@
require 'uri'
require 'net/http'
require 'json'
name = "I love rubies"
payload = STDIN.read
if payload != ""
payload = JSON.parse(payload)
name = payload['name']
end
def open(url)
Net::HTTP.get(URI.parse(url))
end
h = "docker.for.mac.localhost" # ENV['HOSTNAME']
header = open("http://#{h}:8080/r/#{ENV['FN_APP_NAME']}/header") # todo: grab env vars to construct this
puts header
puts "Hello, #{name}! YOOO"
footer = open("http://#{h}:8080/r/#{ENV['FN_APP_NAME']}/footer") # todo: grab env vars to construct this
puts footer

View File

@@ -0,0 +1,7 @@
name: ruby
version: 0.0.23
runtime: ruby
entrypoint: ruby func.rb
headers:
content-type:
- text/html