mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
Blog API example (#377)
This commit is contained in:
34
examples/apps/blog/README.md
Normal file
34
examples/apps/blog/README.md
Normal 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
|
||||
1
examples/apps/blog/app.yaml
Normal file
1
examples/apps/blog/app.yaml
Normal file
@@ -0,0 +1 @@
|
||||
name: blog
|
||||
22
examples/apps/blog/func.go
Normal file
22
examples/apps/blog/func.go
Normal 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))
|
||||
}
|
||||
5
examples/apps/blog/func.yaml
Normal file
5
examples/apps/blog/func.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
version: 0.0.20
|
||||
runtime: go
|
||||
entrypoint: ./func
|
||||
build_image: ""
|
||||
run_image: ""
|
||||
4
examples/apps/blog/post.json
Normal file
4
examples/apps/blog/post.json
Normal 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. "
|
||||
}
|
||||
5
examples/apps/blog/posts/Gemfile
Normal file
5
examples/apps/blog/posts/Gemfile
Normal file
@@ -0,0 +1,5 @@
|
||||
source 'https://rubygems.org'
|
||||
|
||||
gem 'json', '> 2'
|
||||
gem 'sequel'
|
||||
gem 'mysql2'
|
||||
5
examples/apps/blog/posts/create/Gemfile
Normal file
5
examples/apps/blog/posts/create/Gemfile
Normal file
@@ -0,0 +1,5 @@
|
||||
source 'https://rubygems.org'
|
||||
|
||||
gem 'json', '> 2'
|
||||
gem 'sequel'
|
||||
gem 'mysql2'
|
||||
20
examples/apps/blog/posts/create/func.rb
Normal file
20
examples/apps/blog/posts/create/func.rb
Normal 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
|
||||
5
examples/apps/blog/posts/create/func.yaml
Normal file
5
examples/apps/blog/posts/create/func.yaml
Normal 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
|
||||
26
examples/apps/blog/posts/create/test.json
Normal file
26
examples/apps/blog/posts/create/test.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"tests": [
|
||||
{
|
||||
"input": {
|
||||
"body": {
|
||||
"name": "Johnny"
|
||||
}
|
||||
},
|
||||
"output": {
|
||||
"body": {
|
||||
"message": "Hello Johnny"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"input": {
|
||||
"body": ""
|
||||
},
|
||||
"output": {
|
||||
"body": {
|
||||
"message": "Hello World"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
14
examples/apps/blog/posts/func.rb
Normal file
14
examples/apps/blog/posts/func.rb
Normal 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
|
||||
5
examples/apps/blog/posts/func.yaml
Normal file
5
examples/apps/blog/posts/func.yaml
Normal 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
|
||||
26
examples/apps/blog/posts/test.json
Normal file
26
examples/apps/blog/posts/test.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"tests": [
|
||||
{
|
||||
"input": {
|
||||
"body": {
|
||||
"name": "Johnny"
|
||||
}
|
||||
},
|
||||
"output": {
|
||||
"body": {
|
||||
"message": "Hello Johnny"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"input": {
|
||||
"body": ""
|
||||
},
|
||||
"output": {
|
||||
"body": {
|
||||
"message": "Hello World"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
5
examples/apps/blog/schema/Gemfile
Normal file
5
examples/apps/blog/schema/Gemfile
Normal file
@@ -0,0 +1,5 @@
|
||||
source 'https://rubygems.org'
|
||||
|
||||
gem 'json', '> 2'
|
||||
gem 'sequel'
|
||||
gem 'mysql2'
|
||||
10
examples/apps/blog/schema/create.rb
Normal file
10
examples/apps/blog/schema/create.rb
Normal 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
|
||||
5
examples/apps/blog/schema/func.yaml
Normal file
5
examples/apps/blog/schema/func.yaml
Normal 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
|
||||
26
examples/apps/blog/test.json
Normal file
26
examples/apps/blog/test.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"tests": [
|
||||
{
|
||||
"input": {
|
||||
"body": {
|
||||
"name": "Johnny"
|
||||
}
|
||||
},
|
||||
"output": {
|
||||
"body": {
|
||||
"message": "Hello Johnny"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"input": {
|
||||
"body": ""
|
||||
},
|
||||
"output": {
|
||||
"body": {
|
||||
"message": "Hello World"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
5
examples/apps/hellos/README.md
Normal file
5
examples/apps/hellos/README.md
Normal 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.
|
||||
3
examples/apps/hellos/app.yaml
Normal file
3
examples/apps/hellos/app.yaml
Normal file
@@ -0,0 +1,3 @@
|
||||
name: helloapp
|
||||
config:
|
||||
foo: bar
|
||||
9
examples/apps/hellos/footer/func.rb
Normal file
9
examples/apps/hellos/footer/func.rb
Normal 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>
|
||||
}
|
||||
7
examples/apps/hellos/footer/func.yaml
Normal file
7
examples/apps/hellos/footer/func.yaml
Normal file
@@ -0,0 +1,7 @@
|
||||
name: footer
|
||||
version: 0.0.13
|
||||
runtime: ruby
|
||||
entrypoint: ruby func.rb
|
||||
headers:
|
||||
content-type:
|
||||
- text/html
|
||||
58
examples/apps/hellos/func.go
Normal file
58
examples/apps/hellos/func.go
Normal 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)
|
||||
}
|
||||
4
examples/apps/hellos/func.yaml
Normal file
4
examples/apps/hellos/func.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
name: app
|
||||
version: 0.0.70
|
||||
runtime: go
|
||||
entrypoint: ./func
|
||||
9
examples/apps/hellos/header/func.rb
Normal file
9
examples/apps/hellos/header/func.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
puts %{
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>My App</title>
|
||||
</head>
|
||||
<body>
|
||||
}
|
||||
7
examples/apps/hellos/header/func.yaml
Normal file
7
examples/apps/hellos/header/func.yaml
Normal 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
2
examples/apps/hellos/node/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
node_modules/
|
||||
Dockerfile
|
||||
1
examples/apps/hellos/node/README.md
Normal file
1
examples/apps/hellos/node/README.md
Normal file
@@ -0,0 +1 @@
|
||||
# Node function
|
||||
10
examples/apps/hellos/node/func.js
Normal file
10
examples/apps/hellos/node/func.js
Normal 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);
|
||||
4
examples/apps/hellos/node/func.yaml
Normal file
4
examples/apps/hellos/node/func.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
name: node
|
||||
version: 0.0.13
|
||||
runtime: node
|
||||
entrypoint: node func.js
|
||||
7
examples/apps/hellos/node/package.json
Normal file
7
examples/apps/hellos/node/package.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "my-awesome-func",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"is-positive": "^3.1.0"
|
||||
}
|
||||
}
|
||||
1
examples/apps/hellos/python/.gitignore
vendored
Normal file
1
examples/apps/hellos/python/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
packages/
|
||||
1
examples/apps/hellos/python/README.md
Normal file
1
examples/apps/hellos/python/README.md
Normal file
@@ -0,0 +1 @@
|
||||
# Python function
|
||||
21
examples/apps/hellos/python/func.py
Normal file
21
examples/apps/hellos/python/func.py
Normal 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 + "!"
|
||||
4
examples/apps/hellos/python/func.yaml
Normal file
4
examples/apps/hellos/python/func.yaml
Normal 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
3
examples/apps/hellos/ruby/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
bundle/
|
||||
.bundle/
|
||||
Dockerfile
|
||||
3
examples/apps/hellos/ruby/Gemfile
Normal file
3
examples/apps/hellos/ruby/Gemfile
Normal file
@@ -0,0 +1,3 @@
|
||||
source 'https://rubygems.org'
|
||||
|
||||
gem 'json', '> 1.8.2'
|
||||
1
examples/apps/hellos/ruby/README.md
Normal file
1
examples/apps/hellos/ruby/README.md
Normal file
@@ -0,0 +1 @@
|
||||
# Ruby function
|
||||
25
examples/apps/hellos/ruby/func.rb
Normal file
25
examples/apps/hellos/ruby/func.rb
Normal 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
|
||||
7
examples/apps/hellos/ruby/func.yaml
Normal file
7
examples/apps/hellos/ruby/func.yaml
Normal file
@@ -0,0 +1,7 @@
|
||||
name: ruby
|
||||
version: 0.0.23
|
||||
runtime: ruby
|
||||
entrypoint: ruby func.rb
|
||||
headers:
|
||||
content-type:
|
||||
- text/html
|
||||
Reference in New Issue
Block a user