getting-started/background/index/main.go (130 lines of code) (raw):

// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // [START getting_started_background_app_main] // Command index is an HTTP app that displays all previous translations // (stored in Firestore) and has a form to request new translations. On form // submission, the request is sent to Pub/Sub to be processed in the background. package main import ( "context" "encoding/json" "fmt" "html/template" "log" "net/http" "os" "path/filepath" "cloud.google.com/go/firestore" "cloud.google.com/go/pubsub" "github.com/GoogleCloudPlatform/golang-samples/getting-started/background" ) // topicName is the Pub/Sub topic to publish requests to. The Cloud Function to // process translation requests should be subscribed to this topic. const topicName = "translate" // An app holds the clients and parsed templates that can be reused between // requests. type app struct { pubsubClient *pubsub.Client pubsubTopic *pubsub.Topic firestoreClient *firestore.Client tmpl *template.Template } func main() { projectID := os.Getenv("GOOGLE_CLOUD_PROJECT") if projectID == "" { log.Fatalf("GOOGLE_CLOUD_PROJECT must be set") } a, err := newApp(projectID, "index") if err != nil { log.Fatalf("newApp: %v", err) } http.HandleFunc("/", a.index) http.HandleFunc("/request-translation", a.requestTranslation) port := os.Getenv("PORT") if port == "" { port = "8080" } log.Printf("Listening on localhost:%v", port) if err := http.ListenAndServe(":"+port, nil); err != nil { log.Fatal(err) } } // newApp creates a new app. func newApp(projectID, templateDir string) (*app, error) { ctx := context.Background() pubsubClient, err := pubsub.NewClient(ctx, projectID) if err != nil { return nil, fmt.Errorf("pubsub.NewClient: %w", err) } pubsubTopic := pubsubClient.Topic(topicName) firestoreClient, err := firestore.NewClient(ctx, projectID) if err != nil { return nil, fmt.Errorf("firestore.NewClient: %w", err) } // Template referenced relative to the module/app root. tmpl, err := template.ParseFiles(filepath.Join(templateDir, "index.html")) if err != nil { return nil, fmt.Errorf("template.New: %w", err) } return &app{ pubsubClient: pubsubClient, pubsubTopic: pubsubTopic, firestoreClient: firestoreClient, tmpl: tmpl, }, nil } // [END getting_started_background_app_main] // [START getting_started_background_app_list] // index lists the current translations. func (a *app) index(w http.ResponseWriter, r *http.Request) { docs, err := a.firestoreClient.Collection("translations").Documents(r.Context()).GetAll() if err != nil { log.Printf("GetAll: %v", err) http.Error(w, fmt.Sprintf("Error getting translations: %v", err), http.StatusInternalServerError) return } var translations []background.Translation for _, d := range docs { t := background.Translation{} if err := d.DataTo(&t); err != nil { log.Printf("DataTo: %v", err) http.Error(w, "Error reading translations", http.StatusInternalServerError) return } translations = append(translations, t) } if err := a.tmpl.Execute(w, translations); err != nil { log.Printf("tmpl.Execute: %v", err) http.Error(w, "Error writing response", http.StatusInternalServerError) return } } // [END getting_started_background_app_list] // [START getting_started_background_app_request] // requestTranslation parses the request, validates it, and sends it to Pub/Sub. func (a *app) requestTranslation(w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { log.Printf("ParseForm: %v", err) http.Error(w, "Bad request", http.StatusBadRequest) return } v := r.PostFormValue("v") if v == "" { log.Printf("Empty value") http.Error(w, "Empty value", http.StatusBadRequest) return } acceptableLanguages := map[string]bool{ "de": true, "en": true, "es": true, "fr": true, "ja": true, "sw": true, } lang := r.PostFormValue("lang") if !acceptableLanguages[lang] { log.Printf("Unsupported language: %v", lang) http.Error(w, fmt.Sprintf("Unsupported language: %v", lang), http.StatusBadRequest) return } log.Printf("Translation requested: %q -> %s", v, lang) t := background.Translation{ Original: v, Language: lang, } msg, err := json.Marshal(t) if err != nil { log.Printf("json.Marshal: %v", err) http.Error(w, "Error requesting translation", http.StatusInternalServerError) return } res := a.pubsubTopic.Publish(r.Context(), &pubsub.Message{Data: msg}) if _, err := res.Get(r.Context()); err != nil { log.Printf("Publish.Get: %v", err) http.Error(w, "Error requesting translation", http.StatusInternalServerError) return } } // [END getting_started_background_app_request]