<commit_before>//
// 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
//
//     http://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.

package convert

import (
	"archive/tar"
	"compress/gzip"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"

	"github.com/appc/spec/aci"
	"github.com/appc/spec/schema"
)

func buildACI(dir string) (string, error) {
	imageName, err := filepath.Abs(dir)
	if err != nil {
		log.Fatalf("err: %v", err)
	}
	imageName += ".aci"
	err = createACI(dir, imageName)

	return imageName, err
}

func createACI(dir string, imageName string) error {
	var errStr string
	var errRes error
	buildNocompress := true
	root := dir
	tgt := imageName

	ext := filepath.Ext(tgt)
	if ext != schema.ACIExtension {
		errStr = fmt.Sprintf("build: Extension must be %s (given %s)", schema.ACIExtension, ext)
		errRes = errors.New(errStr)
		return errRes
	}

	if err := aci.ValidateLayout(root); err != nil {
		if e, ok := err.(aci.ErrOldVersion); ok {
			if debugEnabled {
				log.Printf("build: Warning: %v. Please update your manifest.", e)
			}
		} else {
			errStr = fmt.Sprintf("build: Layout failed validation: %v", err)
			errRes = errors.New(errStr)
			return errRes
		}
	}

	mode := os.O_CREATE | os.O_WRONLY | os.O_TRUNC
	fh, err := os.OpenFile(tgt, mode, 0644)
	if err != nil {
		errStr = fmt.Sprintf("build: Unable to open target %s: %v", tgt, err)
		errRes = errors.New(errStr)
		return errRes
	}

	var gw *gzip.Writer
	var r io.WriteCloser = fh
	if !buildNocompress {
		gw = gzip.NewWriter(fh)
		r = gw
	}
	tr := tar.NewWriter(r)

	defer func() {
		tr.Close()
		if !buildNocompress {
			gw.Close()
		}
		fh.Close()
	}()

	mpath := filepath.Join(root, aci.ManifestFile)
	b, err := ioutil.ReadFile(mpath)
	if err != nil {
		errStr = fmt.Sprintf("build: Unable to read Image Manifest: %v", err)
		errRes = errors.New(errStr)
		return errRes
	}
	var im schema.ImageManifest
	if err := im.UnmarshalJSON(b); err != nil {
		errStr = fmt.Sprintf("build: Unable to load Image Manifest: %v", err)
		errRes = errors.New(errStr)
		return errRes
	}
	iw := aci.NewImageWriter(im, tr)

	err = filepath.Walk(root, aci.BuildWalker(root, iw))
	if err != nil {
		errStr = fmt.Sprintf("build: Error walking rootfs: %v", err)
		errRes = errors.New(errStr)
		return errRes
	}

	err = iw.Close()
	if err != nil {
		errStr = fmt.Sprintf("build: Unable to close image %s: %v", tgt, err)
		errRes = errors.New(errStr)
		return errRes
	}

	return nil
}
<commit_msg>Adapt to aci function change<commit_after>//
// 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
//
//     http://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.

package convert

import (
	"archive/tar"
	"compress/gzip"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"

	"github.com/appc/spec/aci"
	"github.com/appc/spec/schema"
)

func buildACI(dir string) (string, error) {
	imageName, err := filepath.Abs(dir)
	if err != nil {
		log.Fatalf("err: %v", err)
	}
	imageName += ".aci"
	err = createACI(dir, imageName)

	return imageName, err
}

func createACI(dir string, imageName string) error {
	var errStr string
	var errRes error
	buildNocompress := true
	root := dir
	tgt := imageName

	ext := filepath.Ext(tgt)
	if ext != schema.ACIExtension {
		errStr = fmt.Sprintf("build: Extension must be %s (given %s)", schema.ACIExtension, ext)
		errRes = errors.New(errStr)
		return errRes
	}

	if err := aci.ValidateLayout(root); err != nil {
		if e, ok := err.(aci.ErrOldVersion); ok {
			if debugEnabled {
				log.Printf("build: Warning: %v. Please update your manifest.", e)
			}
		} else {
			errStr = fmt.Sprintf("build: Layout failed validation: %v", err)
			errRes = errors.New(errStr)
			return errRes
		}
	}

	mode := os.O_CREATE | os.O_WRONLY | os.O_TRUNC
	fh, err := os.OpenFile(tgt, mode, 0644)
	if err != nil {
		errStr = fmt.Sprintf("build: Unable to open target %s: %v", tgt, err)
		errRes = errors.New(errStr)
		return errRes
	}

	var gw *gzip.Writer
	var r io.WriteCloser = fh
	if !buildNocompress {
		gw = gzip.NewWriter(fh)
		r = gw
	}
	tr := tar.NewWriter(r)

	defer func() {
		tr.Close()
		if !buildNocompress {
			gw.Close()
		}
		fh.Close()
	}()

	mpath := filepath.Join(root, aci.ManifestFile)
	b, err := ioutil.ReadFile(mpath)
	if err != nil {
		errStr = fmt.Sprintf("build: Unable to read Image Manifest: %v", err)
		errRes = errors.New(errStr)
		return errRes
	}
	var im schema.ImageManifest
	if err := im.UnmarshalJSON(b); err != nil {
		errStr = fmt.Sprintf("build: Unable to load Image Manifest: %v", err)
		errRes = errors.New(errStr)
		return errRes
	}
	iw := aci.NewImageWriter(im, tr)

	err = filepath.Walk(root, aci.BuildWalker(root, iw, nil))
	if err != nil {
		errStr = fmt.Sprintf("build: Error walking rootfs: %v", err)
		errRes = errors.New(errStr)
		return errRes
	}

	err = iw.Close()
	if err != nil {
		errStr = fmt.Sprintf("build: Unable to close image %s: %v", tgt, err)
		errRes = errors.New(errStr)
		return errRes
	}

	return nil
}
<|endoftext|>


<commit_before><commit_msg>Fix bench<commit_after><|endoftext|>


<commit_before>package exchange_actions

import (
	"encoding/csv"
	"fmt"
	"os"
	"path"
	"path/filepath"
	"sort"
	"strings"
	"time"

	"github.com/qor/admin"
	"github.com/qor/i18n"
	"github.com/qor/media_library"
	"github.com/qor/worker"
)

type ExportTranslationArgument struct {
	Scope string
}

type ImportTranslationArgument struct {
	TranslationsFile media_library.FileSystem
}

// RegisterExchangeJobs register i18n jobs into worker
func RegisterExchangeJobs(I18n *i18n.I18n, Worker *worker.Worker) {
	Worker.Admin.RegisterViewPath("github.com/qor/i18n/exchange_actions/views")

	// Export Translations
	exportTranslationResource := Worker.Admin.NewResource(&ExportTranslationArgument{})
	exportTranslationResource.Meta(&admin.Meta{Name: "Scope", Type: "select_one", Collection: []string{"All", "Backend", "Frontend"}})

	Worker.RegisterJob(&worker.Job{
		Name:     "Export Translations",
		Group:    "Export/Import Translations From CSV file",
		Resource: exportTranslationResource,
		Handler: func(arg interface{}, qorJob worker.QorJobInterface) (err error) {
			var (
				locales          []string
				translationKeys  []string
				translationsMap  = map[string]bool{}
				filename         = fmt.Sprintf("/downloads/translations.%v.csv", time.Now().UnixNano())
				fullFilename     = path.Join("public", filename)
				i18nTranslations = I18n.LoadTranslations()
				scope            = arg.(*ExportTranslationArgument).Scope
			)
			qorJob.AddLog("Exporting translations...")

			// Sort locales
			for locale := range i18nTranslations {
				locales = append(locales, locale)
			}
			sort.Strings(locales)

			// Create download file
			if _, err = os.Stat(filepath.Dir(fullFilename)); os.IsNotExist(err) {
				err = os.MkdirAll(filepath.Dir(fullFilename), os.ModePerm)
			}
			csvfile, err := os.OpenFile(fullFilename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
			defer csvfile.Close()
			if err != nil {
				return err
			}

			writer := csv.NewWriter(csvfile)

			// Append Headers
			writer.Write(append([]string{"Translation Keys"}, locales...))

			// Sort translation keys
			for _, locale := range locales {
				for key := range i18nTranslations[locale] {
					translationsMap[key] = true
				}
			}

			for key := range translationsMap {
				translationKeys = append(translationKeys, key)
			}
			sort.Strings(translationKeys)

			// Write CSV file
			for _, translationKey := range translationKeys {
				// Filter out translation by scope
				if scope == "Backend" && !strings.HasPrefix(translationKey, "qor_") {
					continue
				}
				if scope == "Frontend" && strings.HasPrefix(translationKey, "qor_") {
					continue
				}
				var translations = []string{translationKey}
				for _, locale := range locales {
					var value string
					if translation := i18nTranslations[locale][translationKey]; translation != nil {
						value = translation.Value
					}
					translations = append(translations, value)
				}
				writer.Write(translations)
			}
			writer.Flush()

			qorJob.SetProgressText(fmt.Sprintf("Download exported translations", filename))
			return
		},
	})

	// Import Translations

	Worker.RegisterJob(&worker.Job{
		Name:     "Import Translations",
		Group:    "Export/Import Translations From CSV file",
		Resource: Worker.Admin.NewResource(&ImportTranslationArgument{}),
		Handler: func(arg interface{}, qorJob worker.QorJobInterface) (err error) {
			importTranslationArgument := arg.(*ImportTranslationArgument)
			qorJob.AddLog("Importing translations...")
			if csvfile, err := os.Open(path.Join("public", importTranslationArgument.TranslationsFile.URL())); err == nil {
				reader := csv.NewReader(csvfile)
				reader.TrimLeadingSpace = true
				if records, err := reader.ReadAll(); err == nil {
					if len(records) > 1 && len(records[0]) > 1 {
						var (
							recordCount         = len(records) - 1
							perCount            = recordCount/20 + 1
							processedRecordLogs = []string{}
							locales             = records[0][1:]
							index               = 1
						)
						for _, values := range records[1:] {
							logMsg := ""
							for idx, value := range values[1:] {
								if value == "" {
									if values[0] != "" && locales[idx] != "" {
										I18n.DeleteTranslation(&i18n.Translation{
											Key:    values[0],
											Locale: locales[idx],
										})
										logMsg += fmt.Sprintf("%v/%v Deleted %v,%v\n", index, recordCount, locales[idx], values[0])
									}
								} else {
									I18n.SaveTranslation(&i18n.Translation{
										Key:    values[0],
										Locale: locales[idx],
										Value:  value,
									})
									logMsg += fmt.Sprintf("%v/%v Imported %v,%v,%v\n", index, recordCount, locales[idx], values[0], value)
								}
							}
							processedRecordLogs = append(processedRecordLogs, logMsg)
							if len(processedRecordLogs) < perCount {
								qorJob.AddLog(strings.Join(processedRecordLogs, ""))
								processedRecordLogs = []string{}
								qorJob.SetProgress(uint(float32(index) / float32(recordCount+1) * 100))
							}
							index++
						}
						qorJob.AddLog(strings.Join(processedRecordLogs, ""))
					}
				}
				qorJob.AddLog("Imported translations")
			}
			return
		},
	})
}
<commit_msg>Add process log for export translations<commit_after>package exchange_actions

import (
	"encoding/csv"
	"fmt"
	"os"
	"path"
	"path/filepath"
	"sort"
	"strings"
	"time"

	"github.com/qor/admin"
	"github.com/qor/i18n"
	"github.com/qor/media_library"
	"github.com/qor/worker"
)

type ExportTranslationArgument struct {
	Scope string
}

type ImportTranslationArgument struct {
	TranslationsFile media_library.FileSystem
}

// RegisterExchangeJobs register i18n jobs into worker
func RegisterExchangeJobs(I18n *i18n.I18n, Worker *worker.Worker) {
	Worker.Admin.RegisterViewPath("github.com/qor/i18n/exchange_actions/views")

	// Export Translations
	exportTranslationResource := Worker.Admin.NewResource(&ExportTranslationArgument{})
	exportTranslationResource.Meta(&admin.Meta{Name: "Scope", Type: "select_one", Collection: []string{"All", "Backend", "Frontend"}})

	Worker.RegisterJob(&worker.Job{
		Name:     "Export Translations",
		Group:    "Export/Import Translations From CSV file",
		Resource: exportTranslationResource,
		Handler: func(arg interface{}, qorJob worker.QorJobInterface) (err error) {
			var (
				locales          []string
				translationKeys  []string
				translationsMap  = map[string]bool{}
				filename         = fmt.Sprintf("/downloads/translations.%v.csv", time.Now().UnixNano())
				fullFilename     = path.Join("public", filename)
				i18nTranslations = I18n.LoadTranslations()
				scope            = arg.(*ExportTranslationArgument).Scope
			)
			qorJob.AddLog("Exporting translations...")

			// Sort locales
			for locale := range i18nTranslations {
				locales = append(locales, locale)
			}
			sort.Strings(locales)

			// Create download file
			if _, err = os.Stat(filepath.Dir(fullFilename)); os.IsNotExist(err) {
				err = os.MkdirAll(filepath.Dir(fullFilename), os.ModePerm)
			}
			csvfile, err := os.OpenFile(fullFilename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
			defer csvfile.Close()
			if err != nil {
				return err
			}

			writer := csv.NewWriter(csvfile)

			// Append Headers
			writer.Write(append([]string{"Translation Keys"}, locales...))

			// Sort translation keys
			for _, locale := range locales {
				for key := range i18nTranslations[locale] {
					translationsMap[key] = true
				}
			}

			for key := range translationsMap {
				translationKeys = append(translationKeys, key)
			}
			sort.Strings(translationKeys)

			// Write CSV file
			var (
				recordCount         = len(translationKeys)
				perCount            = recordCount/20 + 1
				processedRecordLogs = []string{}
				index               = 0
				progressCount       = 0
			)
			for _, translationKey := range translationKeys {
				// Filter out translation by scope
				index++
				if scope == "Backend" && !strings.HasPrefix(translationKey, "qor_") {
					continue
				}
				if scope == "Frontend" && strings.HasPrefix(translationKey, "qor_") {
					continue
				}
				var translations = []string{translationKey}
				for _, locale := range locales {
					var value string
					if translation := i18nTranslations[locale][translationKey]; translation != nil {
						value = translation.Value
					}
					translations = append(translations, value)
				}
				writer.Write(translations)
				processedRecordLogs = append(processedRecordLogs, fmt.Sprintf("Exported %v\n", strings.Join(translations, ",")))
				if index == perCount {
					qorJob.AddLog(strings.Join(processedRecordLogs, ""))
					processedRecordLogs = []string{}
					progressCount++
					qorJob.SetProgress(uint(float32(progressCount) / float32(20) * 100))
					index = 0
				}
			}
			writer.Flush()

			qorJob.SetProgressText(fmt.Sprintf("Download exported translations", filename))
			return
		},
	})

	// Import Translations

	Worker.RegisterJob(&worker.Job{
		Name:     "Import Translations",
		Group:    "Export/Import Translations From CSV file",
		Resource: Worker.Admin.NewResource(&ImportTranslationArgument{}),
		Handler: func(arg interface{}, qorJob worker.QorJobInterface) (err error) {
			importTranslationArgument := arg.(*ImportTranslationArgument)
			qorJob.AddLog("Importing translations...")
			if csvfile, err := os.Open(path.Join("public", importTranslationArgument.TranslationsFile.URL())); err == nil {
				reader := csv.NewReader(csvfile)
				reader.TrimLeadingSpace = true
				if records, err := reader.ReadAll(); err == nil {
					if len(records) > 1 && len(records[0]) > 1 {
						var (
							recordCount         = len(records) - 1
							perCount            = recordCount/20 + 1
							processedRecordLogs = []string{}
							locales             = records[0][1:]
							index               = 1
						)
						for _, values := range records[1:] {
							logMsg := ""
							for idx, value := range values[1:] {
								if value == "" {
									if values[0] != "" && locales[idx] != "" {
										I18n.DeleteTranslation(&i18n.Translation{
											Key:    values[0],
											Locale: locales[idx],
										})
										logMsg += fmt.Sprintf("%v/%v Deleted %v,%v\n", index, recordCount, locales[idx], values[0])
									}
								} else {
									I18n.SaveTranslation(&i18n.Translation{
										Key:    values[0],
										Locale: locales[idx],
										Value:  value,
									})
									logMsg += fmt.Sprintf("%v/%v Imported %v,%v,%v\n", index, recordCount, locales[idx], values[0], value)
								}
							}
							processedRecordLogs = append(processedRecordLogs, logMsg)
							if len(processedRecordLogs) == perCount {
								qorJob.AddLog(strings.Join(processedRecordLogs, ""))
								processedRecordLogs = []string{}
								qorJob.SetProgress(uint(float32(index) / float32(recordCount+1) * 100))
							}
							index++
						}
						qorJob.AddLog(strings.Join(processedRecordLogs, ""))
					}
				}
				qorJob.AddLog("Imported translations")
			}
			return
		},
	})
}
<|endoftext|>


<commit_before>package exchange_actions

import (
	"encoding/csv"
	"fmt"
	"os"
	"path"
	"path/filepath"
	"sort"
	"strings"
	"time"

	"github.com/qor/i18n"
	"github.com/qor/media_library"
	"github.com/qor/qor/admin"
	"github.com/qor/worker"
)

func RegisterExchangeJobs(I18n *i18n.I18n, Worker *worker.Worker) {
	for _, gopath := range strings.Split(os.Getenv("GOPATH"), ":") {
		admin.RegisterViewPath(path.Join(gopath, "src/github.com/qor/i18n/exchange_actions/views"))
	}

	Worker.RegisterJob(worker.Job{
		Name:  "Export Translations",
		Group: "Translations",
		Handler: func(arg interface{}, qorJob worker.QorJobInterface) (err error) {
			var (
				locales         []string
				translationKeys []string
				translationsMap = map[string]bool{}
				filename        = fmt.Sprintf("/downloads/translations.%v.csv", time.Now().UnixNano())
				fullFilename    = path.Join("public", filename)
			)
			qorJob.AddLog("Exporting translations...")

			// Sort locales
			for locale, _ := range I18n.Translations {
				locales = append(locales, locale)
			}
			sort.Strings(locales)

			// Create download file
			if _, err = os.Stat(filepath.Dir(fullFilename)); os.IsNotExist(err) {
				err = os.MkdirAll(filepath.Dir(fullFilename), os.ModePerm)
			}
			csvfile, err := os.OpenFile(fullFilename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
			defer csvfile.Close()
			if err != nil {
				return err
			}

			writer := csv.NewWriter(csvfile)

			// Append Headers
			writer.Write(append([]string{"Translation Keys"}, locales...))

			// Sort translation keys
			for _, locale := range locales {
				for key, _ := range I18n.Translations[locale] {
					translationsMap[key] = true
				}
			}

			for key := range translationsMap {
				translationKeys = append(translationKeys, key)
			}
			sort.Strings(translationKeys)

			// Write CSV file
			for _, translationKey := range translationKeys {
				var translations = []string{translationKey}
				for _, locale := range locales {
					var value string
					if translation := I18n.Translations[locale][translationKey]; translation != nil {
						value = translation.Value
					}
					translations = append(translations, value)
				}
				writer.Write(translations)
			}
			writer.Flush()

			qorJob.SetProgressText(fmt.Sprintf("Download exported translations", filename))
			return
		},
	})

	// Import Translations
	type importTranslationArgument struct {
		Translations media_library.FileSystem
	}

	Worker.RegisterJob(worker.Job{
		Name:     "Import Translations",
		Group:    "Translations",
		Resource: Worker.Admin.NewResource(&importTranslationArgument{}),
		Handler: func(arg interface{}, qorJob worker.QorJobInterface) (err error) {
			importTranslationArgument := arg.(*importTranslationArgument)
			qorJob.AddLog("Importing translations...")
			if csvfile, err := os.Open(path.Join("public", importTranslationArgument.Translations.URL())); err == nil {
				reader := csv.NewReader(csvfile)
				reader.TrimLeadingSpace = true
				if records, err := reader.ReadAll(); err == nil {
					if len(records) > 1 && len(records[0]) > 1 {
						locales := records[0][1:]

						for _, values := range records[1:] {
							for idx, value := range values[1:] {
								if value == "" {
									I18n.DeleteTranslation(&i18n.Translation{
										Key:    values[0],
										Locale: locales[idx],
									})
								} else {
									I18n.SaveTranslation(&i18n.Translation{
										Key:    values[0],
										Locale: locales[idx],
										Value:  value,
									})
								}
							}
						}
					}
				}
				qorJob.AddLog("Imported translations")
			}
			return
		},
	})
}
<commit_msg>Rename group name<commit_after>package exchange_actions

import (
	"encoding/csv"
	"fmt"
	"os"
	"path"
	"path/filepath"
	"sort"
	"strings"
	"time"

	"github.com/qor/i18n"
	"github.com/qor/media_library"
	"github.com/qor/qor/admin"
	"github.com/qor/worker"
)

func RegisterExchangeJobs(I18n *i18n.I18n, Worker *worker.Worker) {
	for _, gopath := range strings.Split(os.Getenv("GOPATH"), ":") {
		admin.RegisterViewPath(path.Join(gopath, "src/github.com/qor/i18n/exchange_actions/views"))
	}

	Worker.RegisterJob(worker.Job{
		Name:  "Export Translations",
		Group: "Export/Import Translations From CSV file",
		Handler: func(arg interface{}, qorJob worker.QorJobInterface) (err error) {
			var (
				locales         []string
				translationKeys []string
				translationsMap = map[string]bool{}
				filename        = fmt.Sprintf("/downloads/translations.%v.csv", time.Now().UnixNano())
				fullFilename    = path.Join("public", filename)
			)
			qorJob.AddLog("Exporting translations...")

			// Sort locales
			for locale, _ := range I18n.Translations {
				locales = append(locales, locale)
			}
			sort.Strings(locales)

			// Create download file
			if _, err = os.Stat(filepath.Dir(fullFilename)); os.IsNotExist(err) {
				err = os.MkdirAll(filepath.Dir(fullFilename), os.ModePerm)
			}
			csvfile, err := os.OpenFile(fullFilename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
			defer csvfile.Close()
			if err != nil {
				return err
			}

			writer := csv.NewWriter(csvfile)

			// Append Headers
			writer.Write(append([]string{"Translation Keys"}, locales...))

			// Sort translation keys
			for _, locale := range locales {
				for key, _ := range I18n.Translations[locale] {
					translationsMap[key] = true
				}
			}

			for key := range translationsMap {
				translationKeys = append(translationKeys, key)
			}
			sort.Strings(translationKeys)

			// Write CSV file
			for _, translationKey := range translationKeys {
				var translations = []string{translationKey}
				for _, locale := range locales {
					var value string
					if translation := I18n.Translations[locale][translationKey]; translation != nil {
						value = translation.Value
					}
					translations = append(translations, value)
				}
				writer.Write(translations)
			}
			writer.Flush()

			qorJob.SetProgressText(fmt.Sprintf("Download exported translations", filename))
			return
		},
	})

	// Import Translations
	type importTranslationArgument struct {
		TranslationsFile media_library.FileSystem
	}

	Worker.RegisterJob(worker.Job{
		Name:     "Import Translations",
		Group:    "Export/Import Translations From CSV file",
		Resource: Worker.Admin.NewResource(&importTranslationArgument{}),
		Handler: func(arg interface{}, qorJob worker.QorJobInterface) (err error) {
			importTranslationArgument := arg.(*importTranslationArgument)
			qorJob.AddLog("Importing translations...")
			if csvfile, err := os.Open(path.Join("public", importTranslationArgument.TranslationsFile.URL())); err == nil {
				reader := csv.NewReader(csvfile)
				reader.TrimLeadingSpace = true
				if records, err := reader.ReadAll(); err == nil {
					if len(records) > 1 && len(records[0]) > 1 {
						locales := records[0][1:]

						for _, values := range records[1:] {
							for idx, value := range values[1:] {
								if value == "" {
									I18n.DeleteTranslation(&i18n.Translation{
										Key:    values[0],
										Locale: locales[idx],
									})
								} else {
									I18n.SaveTranslation(&i18n.Translation{
										Key:    values[0],
										Locale: locales[idx],
										Value:  value,
									})
								}
							}
						}
					}
				}
				qorJob.AddLog("Imported translations")
			}
			return
		},
	})
}
<|endoftext|>


<commit_before><commit_msg>start go version<commit_after><|endoftext|>


<commit_before>package api

import (
	"errors"
	"github.com/grafana/grafana/pkg/bus"
	"github.com/grafana/grafana/pkg/log"
	"github.com/grafana/grafana/pkg/middleware"
	m "github.com/grafana/grafana/pkg/models"
	"strconv"
)

func ValidateOrgPlaylist(c *middleware.Context) {
	id := c.ParamsInt64(":id")
	query := m.GetPlaylistByIdQuery{Id: id}
	err := bus.Dispatch(&query)

	if err != nil {
		c.JsonApiErr(404, "Playlist not found", err)
		return
	}

	if query.Result.OrgId != c.OrgId {
		c.JsonApiErr(403, "You are not allowed to edit/view playlist", nil)
		return
	}
}

func SearchPlaylists(c *middleware.Context) Response {
	query := c.Query("query")
	limit := c.QueryInt("limit")

	if limit == 0 {
		limit = 1000
	}

	searchQuery := m.PlaylistQuery{
		Title: query,
		Limit: limit,
		OrgId: c.OrgId,
	}

	err := bus.Dispatch(&searchQuery)
	if err != nil {
		return ApiError(500, "Search failed", err)
	}

	return Json(200, searchQuery.Result)
}

func GetPlaylist(c *middleware.Context) Response {
	id := c.ParamsInt64(":id")
	cmd := m.GetPlaylistByIdQuery{Id: id}

	if err := bus.Dispatch(&cmd); err != nil {
		return ApiError(500, "Playlist not found", err)
	}

	itemQuery := m.GetPlaylistItemsByIdQuery{PlaylistId: id}
	if err := bus.Dispatch(&itemQuery); err != nil {
		log.Warn("itemQuery failed: %v", err)
		return ApiError(500, "Playlist items not found", err)
	}

	playlistDTOs := make([]m.PlaylistItemDTO, 0)

	for _, item := range *itemQuery.Result {
		playlistDTOs = append(playlistDTOs, m.PlaylistItemDTO{
			Id:         item.Id,
			PlaylistId: item.PlaylistId,
			Type:       item.Type,
			Value:      item.Value,
			Order:      item.Order,
		})
	}

	dto := &m.PlaylistDTO{
		Id:       cmd.Result.Id,
		Title:    cmd.Result.Title,
		Timespan: cmd.Result.Timespan,
		OrgId:    cmd.Result.OrgId,
		Items:    playlistDTOs,
	}

	return Json(200, dto)
}

func LoadPlaylistItems(id int64) ([]m.PlaylistItem, error) {
	itemQuery := m.GetPlaylistItemsByIdQuery{PlaylistId: id}
	if err := bus.Dispatch(&itemQuery); err != nil {
		log.Warn("itemQuery failed: %v", err)
		return nil, errors.New("Playlist not found")
	}

	return *itemQuery.Result, nil
}

func LoadPlaylistDashboards(id int64) ([]m.PlaylistDashboardDto, error) {
	playlistItems, _ := LoadPlaylistItems(id)

	dashboardIds := make([]int64, 0)

	for _, i := range playlistItems {
		dashboardId, _ := strconv.ParseInt(i.Value, 10, 64)
		dashboardIds = append(dashboardIds, dashboardId)
	}

	if len(dashboardIds) == 0 {
		return make([]m.PlaylistDashboardDto, 0), nil
	}

	dashboardQuery := m.GetPlaylistDashboardsQuery{DashboardIds: dashboardIds}
	if err := bus.Dispatch(&dashboardQuery); err != nil {
		log.Warn("dashboardquery failed: %v", err)
		return nil, errors.New("Playlist not found")
	}

	dtos := make([]m.PlaylistDashboardDto, 0)
	for _, item := range *dashboardQuery.Result {
		dtos = append(dtos, m.PlaylistDashboardDto{
			Id:    item.Id,
			Slug:  item.Slug,
			Title: item.Title,
			Uri:   "db/" + item.Slug,
		})
	}

	return dtos, nil
}

func GetPlaylistItems(c *middleware.Context) Response {
	id := c.ParamsInt64(":id")

	items, err := LoadPlaylistItems(id)

	if err != nil {
		return ApiError(500, "Could not load playlist items", err)
	}

	playlistDTOs := make([]m.PlaylistItemDTO, 0)

	for _, item := range items {
		playlistDTOs = append(playlistDTOs, m.PlaylistItemDTO{
			Id:         item.Id,
			PlaylistId: item.PlaylistId,
			Type:       item.Type,
			Value:      item.Value,
			Order:      item.Order,
			Title:      item.Title,
		})
	}

	return Json(200, playlistDTOs)
}

func GetPlaylistDashboards(c *middleware.Context) Response {
	id := c.ParamsInt64(":id")

	playlists, err := LoadPlaylistDashboards(id)
	if err != nil {
		return ApiError(500, "Could not load dashboards", err)
	}

	return Json(200, playlists)
}

func DeletePlaylist(c *middleware.Context) Response {
	id := c.ParamsInt64(":id")

	cmd := m.DeletePlaylistQuery{Id: id}
	if err := bus.Dispatch(&cmd); err != nil {
		return ApiError(500, "Failed to delete playlist", err)
	}

	return Json(200, "")
}

func CreatePlaylist(c *middleware.Context, query m.CreatePlaylistQuery) Response {
	query.OrgId = c.OrgId
	err := bus.Dispatch(&query)
	if err != nil {
		return ApiError(500, "Failed to create playlist", err)
	}

	return Json(200, query.Result)
}

func UpdatePlaylist(c *middleware.Context, query m.UpdatePlaylistQuery) Response {
	err := bus.Dispatch(&query)
	if err != nil {
		return ApiError(500, "Failed to save playlist", err)
	}

	items, err := LoadPlaylistItems(query.Id)

	playlistDTOs := make([]m.PlaylistItemDTO, 0)

	for _, item := range items {
		playlistDTOs = append(playlistDTOs, m.PlaylistItemDTO{
			Id:         item.Id,
			PlaylistId: item.PlaylistId,
			Type:       item.Type,
			Value:      item.Value,
			Order:      item.Order,
		})
	}

	if err != nil {
		return ApiError(500, "Failed to save playlist", err)
	}

	query.Result.Items = playlistDTOs

	return Json(200, query.Result)
}
<commit_msg>style(playlist): abstract DTO creation<commit_after>package api

import (
	"errors"
	"github.com/grafana/grafana/pkg/bus"
	"github.com/grafana/grafana/pkg/log"
	"github.com/grafana/grafana/pkg/middleware"
	m "github.com/grafana/grafana/pkg/models"
	"strconv"
)

func ValidateOrgPlaylist(c *middleware.Context) {
	id := c.ParamsInt64(":id")
	query := m.GetPlaylistByIdQuery{Id: id}
	err := bus.Dispatch(&query)

	if err != nil {
		c.JsonApiErr(404, "Playlist not found", err)
		return
	}

	if query.Result.OrgId != c.OrgId {
		c.JsonApiErr(403, "You are not allowed to edit/view playlist", nil)
		return
	}
}

func SearchPlaylists(c *middleware.Context) Response {
	query := c.Query("query")
	limit := c.QueryInt("limit")

	if limit == 0 {
		limit = 1000
	}

	searchQuery := m.PlaylistQuery{
		Title: query,
		Limit: limit,
		OrgId: c.OrgId,
	}

	err := bus.Dispatch(&searchQuery)
	if err != nil {
		return ApiError(500, "Search failed", err)
	}

	return Json(200, searchQuery.Result)
}

func GetPlaylist(c *middleware.Context) Response {
	id := c.ParamsInt64(":id")
	cmd := m.GetPlaylistByIdQuery{Id: id}

	if err := bus.Dispatch(&cmd); err != nil {
		return ApiError(500, "Playlist not found", err)
	}

	playlistDTOs, _ := LoadPlaylistItemDTOs(id)

	dto := &m.PlaylistDTO{
		Id:       cmd.Result.Id,
		Title:    cmd.Result.Title,
		Timespan: cmd.Result.Timespan,
		OrgId:    cmd.Result.OrgId,
		Items:    playlistDTOs,
	}

	return Json(200, dto)
}

func LoadPlaylistItemDTOs(id int64) ([]m.PlaylistItemDTO, error) {
	playlistitems, err := LoadPlaylistItems(id)

	if err != nil {
		return nil, err
	}

	playlistDTOs := make([]m.PlaylistItemDTO, 0)

	for _, item := range playlistitems {
		playlistDTOs = append(playlistDTOs, m.PlaylistItemDTO{
			Id:         item.Id,
			PlaylistId: item.PlaylistId,
			Type:       item.Type,
			Value:      item.Value,
			Order:      item.Order,
			Title:      item.Title,
		})
	}

	return playlistDTOs, nil
}

func LoadPlaylistItems(id int64) ([]m.PlaylistItem, error) {
	itemQuery := m.GetPlaylistItemsByIdQuery{PlaylistId: id}
	if err := bus.Dispatch(&itemQuery); err != nil {
		log.Warn("itemQuery failed: %v", err)
		return nil, errors.New("Playlist not found")
	}

	return *itemQuery.Result, nil
}

func LoadPlaylistDashboards(id int64) ([]m.PlaylistDashboardDto, error) {
	playlistItems, _ := LoadPlaylistItems(id)

	dashboardIds := make([]int64, 0)

	for _, i := range playlistItems {
		dashboardId, _ := strconv.ParseInt(i.Value, 10, 64)
		dashboardIds = append(dashboardIds, dashboardId)
	}

	if len(dashboardIds) == 0 {
		return make([]m.PlaylistDashboardDto, 0), nil
	}

	dashboardQuery := m.GetPlaylistDashboardsQuery{DashboardIds: dashboardIds}
	if err := bus.Dispatch(&dashboardQuery); err != nil {
		log.Warn("dashboardquery failed: %v", err)
		return nil, errors.New("Playlist not found")
	}

	dtos := make([]m.PlaylistDashboardDto, 0)
	for _, item := range *dashboardQuery.Result {
		dtos = append(dtos, m.PlaylistDashboardDto{
			Id:    item.Id,
			Slug:  item.Slug,
			Title: item.Title,
			Uri:   "db/" + item.Slug,
		})
	}

	return dtos, nil
}

func GetPlaylistItems(c *middleware.Context) Response {
	id := c.ParamsInt64(":id")

	playlistDTOs, err := LoadPlaylistItemDTOs(id)

	if err != nil {
		return ApiError(500, "Could not load playlist items", err)
	}

	return Json(200, playlistDTOs)
}

func GetPlaylistDashboards(c *middleware.Context) Response {
	id := c.ParamsInt64(":id")

	playlists, err := LoadPlaylistDashboards(id)
	if err != nil {
		return ApiError(500, "Could not load dashboards", err)
	}

	return Json(200, playlists)
}

func DeletePlaylist(c *middleware.Context) Response {
	id := c.ParamsInt64(":id")

	cmd := m.DeletePlaylistQuery{Id: id}
	if err := bus.Dispatch(&cmd); err != nil {
		return ApiError(500, "Failed to delete playlist", err)
	}

	return Json(200, "")
}

func CreatePlaylist(c *middleware.Context, query m.CreatePlaylistQuery) Response {
	query.OrgId = c.OrgId
	err := bus.Dispatch(&query)
	if err != nil {
		return ApiError(500, "Failed to create playlist", err)
	}

	return Json(200, query.Result)
}

func UpdatePlaylist(c *middleware.Context, query m.UpdatePlaylistQuery) Response {
	err := bus.Dispatch(&query)
	if err != nil {
		return ApiError(500, "Failed to save playlist", err)
	}

	playlistDTOs, err := LoadPlaylistItemDTOs(query.Id)
	if err != nil {
		return ApiError(500, "Failed to save playlist", err)
	}

	query.Result.Items = playlistDTOs

	return Json(200, query.Result)
}
<|endoftext|>


<commit_before><commit_msg>added -g test project gen option<commit_after><|endoftext|>


<commit_before>// Copyright The OpenTelemetry Authors
//
// 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
//
//       http://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.

package conventions

// OpenTelemetry Semantic Convention values for Resource attribute names.
// See: https://github.com/open-telemetry/opentelemetry-specification/tree/main/specification/resource/semantic_conventions/README.md
const (
	AttributeCloudAccount               = "cloud.account.id"
	AttributeCloudProvider              = "cloud.provider"
	AttributeCloudRegion                = "cloud.region"
	AttributeCloudZone                  = "cloud.zone"
	AttributeCloudInfrastructureService = "cloud.infrastructure_service"
	AttributeContainerID                = "container.id"
	AttributeContainerImage             = "container.image.name"
	AttributeContainerName              = "container.name"
	AttributeContainerTag               = "container.image.tag"
	AttributeDeploymentEnvironment      = "deployment.environment"
	AttributeFaasID                     = "faas.id"
	AttributeFaasInstance               = "faas.instance"
	AttributeFaasName                   = "faas.name"
	AttributeFaasVersion                = "faas.version"
	AttributeHostID                     = "host.id"
	AttributeHostImageID                = "host.image.id"
	AttributeHostImageName              = "host.image.name"
	AttributeHostImageVersion           = "host.image.version"
	AttributeHostName                   = "host.name"
	AttributeHostType                   = "host.type"
	AttributeK8sCluster                 = "k8s.cluster.name"
	AttributeK8sContainer               = "k8s.container.name"
	AttributeK8sCronJob                 = "k8s.cronjob.name"
	AttributeK8sCronJobUID              = "k8s.cronjob.uid"
	AttributeK8sDaemonSet               = "k8s.daemonset.name"
	AttributeK8sDaemonSetUID            = "k8s.daemonset.uid"
	AttributeK8sDeployment              = "k8s.deployment.name"
	AttributeK8sDeploymentUID           = "k8s.deployment.uid"
	AttributeK8sJob                     = "k8s.job.name"
	AttributeK8sJobUID                  = "k8s.job.uid"
	AttributeK8sNamespace               = "k8s.namespace.name"
	AttributeK8sPod                     = "k8s.pod.name"
	AttributeK8sPodUID                  = "k8s.pod.uid"
	AttributeK8sReplicaSet              = "k8s.replicaset.name"
	AttributeK8sReplicaSetUID           = "k8s.replicaset.uid"
	AttributeK8sStatefulSet             = "k8s.statefulset.name"
	AttributeK8sStatefulSetUID          = "k8s.statefulset.uid"
	AttributeOSType                     = "os.type"
	AttributeOSDescription              = "os.description"
	AttributeProcessCommand             = "process.command"
	AttributeProcessCommandLine         = "process.command_line"
	AttributeProcessExecutableName      = "process.executable.name"
	AttributeProcessExecutablePath      = "process.executable.path"
	AttributeProcessID                  = "process.pid"
	AttributeProcessOwner               = "process.owner"
	AttributeServiceInstance            = "service.instance.id"
	AttributeServiceName                = "service.name"
	AttributeServiceNamespace           = "service.namespace"
	AttributeServiceVersion             = "service.version"
	AttributeTelemetryAutoVersion       = "telemetry.auto.version"
	AttributeTelemetrySDKLanguage       = "telemetry.sdk.language"
	AttributeTelemetrySDKName           = "telemetry.sdk.name"
	AttributeTelemetrySDKVersion        = "telemetry.sdk.version"
)

// OpenTelemetry Semantic Convention values for Resource attribute "telemetry.sdk.language" values.
// See: https://github.com/open-telemetry/opentelemetry-specification/tree/main/specification/resource/semantic_conventions/README.md
const (
	AttributeSDKLangValueCPP    = "cpp"
	AttributeSDKLangValueDotNET = "dotnet"
	AttributeSDKLangValueErlang = "erlang"
	AttributeSDKLangValueGo     = "go"
	AttributeSDKLangValueJava   = "java"
	AttributeSDKLangValueNodeJS = "nodejs"
	AttributeSDKLangValuePHP    = "php"
	AttributeSDKLangValuePython = "python"
	AttributeSDKLangValueRuby   = "ruby"
	AttributeSDKLangValueWebJS  = "webjs"
)

// OpenTelemetry Semantic Convention values for Resource attribute "cloud.provider" values.
// See: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/semantic_conventions/cloud.md
const (
	AttributeCloudProviderAWS   = "aws"
	AttributeCloudProviderAzure = "azure"
	AttributeCloudProviderGCP   = "gcp"
)

// OpenTelemetry Semantic Convention values for Resource attribute "cloud.infrastructure_service" values.
// See: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/semantic_conventions/cloud.md
const (
	AttributeCloudProviderAWSEC2                  = "aws_ec2"
	AttributeCloudProviderAWSECS                  = "aws_ecs"
	AttributeCloudProviderAWSEKS                  = "aws_eks"
	AttributeCloudProviderAWSLambda               = "aws_lambda"
	AttributeCloudProviderAWSElasticBeanstalk     = "aws_elastic_beanstalk"
	AttributeCloudProviderAzureVM                 = "azure_vm"
	AttributeCloudProviderAzureContainerInstances = "azure_container_instances"
	AttributeCloudProviderAzureAKS                = "azure_aks"
	AttributeCloudProviderAzureFunctions          = "azure_functions"
	AttributeCloudProviderAzureAppService         = "azure_app_service"
	AttributeCloudProviderGCPComputeEngine        = "gcp_compute_engine"
	AttributeCloudProviderGCPCloudRun             = "gcp_cloud_run"
	AttributeCloudProviderGCPGKE                  = "gcp_gke"
	AttributeCloudProviderGCPCloudFunctions       = "gcp_cloud_functions"
	AttributeCloudProviderGCPAppEngine            = "gcp_app_engine"
)

// GetResourceSemanticConventionAttributeNames a slice with all the Resource Semantic Conventions attribute names.
func GetResourceSemanticConventionAttributeNames() []string {
	return []string{
		AttributeCloudAccount,
		AttributeCloudProvider,
		AttributeCloudRegion,
		AttributeCloudZone,
		AttributeCloudInfrastructureService,
		AttributeContainerID,
		AttributeContainerImage,
		AttributeContainerName,
		AttributeContainerTag,
		AttributeDeploymentEnvironment,
		AttributeFaasID,
		AttributeFaasInstance,
		AttributeFaasName,
		AttributeFaasVersion,
		AttributeHostID,
		AttributeHostImageID,
		AttributeHostImageName,
		AttributeHostImageVersion,
		AttributeHostName,
		AttributeHostType,
		AttributeK8sCluster,
		AttributeK8sContainer,
		AttributeK8sCronJob,
		AttributeK8sCronJobUID,
		AttributeK8sDaemonSet,
		AttributeK8sDaemonSetUID,
		AttributeK8sDeployment,
		AttributeK8sDeploymentUID,
		AttributeK8sJob,
		AttributeK8sJobUID,
		AttributeK8sNamespace,
		AttributeK8sPod,
		AttributeK8sPodUID,
		AttributeK8sReplicaSet,
		AttributeK8sReplicaSetUID,
		AttributeK8sStatefulSet,
		AttributeK8sStatefulSetUID,
		AttributeOSType,
		AttributeOSDescription,
		AttributeProcessCommand,
		AttributeProcessCommandLine,
		AttributeProcessExecutableName,
		AttributeProcessExecutablePath,
		AttributeProcessID,
		AttributeProcessOwner,
		AttributeServiceInstance,
		AttributeServiceName,
		AttributeServiceNamespace,
		AttributeServiceVersion,
		AttributeTelemetryAutoVersion,
		AttributeTelemetrySDKLanguage,
		AttributeTelemetrySDKName,
		AttributeTelemetrySDKVersion,
	}
}

// OpenTelemetry Semantic Convention values for general Span attribute names.
// See: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/span-general.md
const (
	AttributeComponent    = "component"
	AttributeEnduserID    = "enduser.id"
	AttributeEnduserRole  = "enduser.role"
	AttributeEnduserScope = "enduser.scope"
	AttributeNetHostIP    = "net.host.ip"
	AttributeNetHostName  = "net.host.name"
	AttributeNetHostPort  = "net.host.port"
	AttributeNetPeerIP    = "net.peer.ip"
	AttributeNetPeerName  = "net.peer.name"
	AttributeNetPeerPort  = "net.peer.port"
	AttributeNetTransport = "net.transport"
	AttributePeerService  = "peer.service"
)

// OpenTelemetry Semantic Convention values for component attribute values.
// Possibly being removed due to issue #336
const (
	ComponentTypeHTTP = "http"
	ComponentTypeGRPC = "grpc"
)

// OpenTelemetry Semantic Convention attribute names for HTTP related attributes
// See: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md
const (
	AttributeHTTPClientIP                          = "http.client_ip"
	AttributeHTTPFlavor                            = "http.flavor"
	AttributeHTTPHost                              = "http.host"
	AttributeHTTPHostName                          = "host.name"
	AttributeHTTPHostPort                          = "host.port"
	AttributeHTTPMethod                            = "http.method"
	AttributeHTTPRequestContentLength              = "http.request_content_length"
	AttributeHTTPRequestContentLengthUncompressed  = "http.request_content_length_uncompressed"
	AttributeHTTPResponseContentLength             = "http.response_content_length"
	AttributeHTTPResponseContentLengthUncompressed = "http.response_content_length_uncompressed"
	AttributeHTTPRoute                             = "http.route"
	AttributeHTTPScheme                            = "http.scheme"
	AttributeHTTPServerName                        = "http.server_name"
	AttributeHTTPStatusCode                        = "http.status_code"
	AttributeHTTPStatusText                        = "http.status_text"
	AttributeHTTPTarget                            = "http.target"
	AttributeHTTPURL                               = "http.url"
	AttributeHTTPUserAgent                         = "http.user_agent"
)

// OpenTelemetry Semantic Convention attribute names for database related attributes
// See: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/database.md
const (
	AttributeDBConnectionString = "db.connection_string"

	AttributeDBCassandraKeyspace   = "db.cassandra.keyspace"
	AttributeDBHBaseNamespace      = "db.hbase.namespace"
	AttributeDBJDBCDriverClassname = "db.jdbc.driver_classname"
	AttributeDBMongoDBCollection   = "db.mongodb.collection"
	AttributeDBMsSQLInstanceName   = "db.mssql.instance_name"

	AttributeDBName               = "db.name"
	AttributeDBOperation          = "db.operation"
	AttributeDBRedisDatabaseIndex = "db.redis.database_index"
	AttributeDBStatement          = "db.statement"
	AttributeDBSystem             = "db.system"
	AttributeDBUser               = "db.user"
)

// OpenTelemetry Semantic Convention attribute names for gRPC related attributes
// See: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/rpc.md
const (
	AttributeMessageCompressedSize   = "message.compressed_size"
	AttributeMessageID               = "message.id"
	AttributeMessageType             = "message.type"
	AttributeMessageUncompressedSize = "message.uncompressed_size"
	AttributeRPCMethod               = "rpc.method"
	AttributeRPCService              = "rpc.service"
	AttributeRPCSystem               = "rpc.system"
	EventTypeMessage                 = "message"
	MessageTypeReceived              = "RECEIVED"
	MessageTypeSent                  = "SENT"
)

// OpenTelemetry Semantic Convention attribute names for FaaS related attributes
// See: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/faas.md
const (
	AttributeFaaSCron               = "faas.cron"
	AttributeFaaSDocumentCollection = "faas.document.collection"
	AttributeFaaSDocumentName       = "faas.document.name"
	AttributeFaaSDocumentOperation  = "faas.document.operation"
	AttributeFaaSDocumentTime       = "faas.document.time"
	AttributeFaaSExecution          = "faas.execution"
	AttributeFaaSTime               = "faas.time"
	AttributeFaaSTrigger            = "faas.trigger"
	FaaSTriggerDataSource           = "datasource"
	FaaSTriggerHTTP                 = "http"
	FaaSTriggerOther                = "other"
	FaaSTriggerPubSub               = "pubsub"
	FaaSTriggerTimer                = "timer"
)

// OpenTelemetry Semantic Convention attribute names for messaging system related attributes
// See: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/messaging.md
const (
	AttributeMessagingConversationID        = "messaging.conversation_id"
	AttributeMessagingDestination           = "messaging.destination"
	AttributeMessagingDestinationKind       = "messaging.destination_kind"
	AttributeMessagingMessageID             = "messaging.message_id"
	AttributeMessagingOperation             = "messaging.operation"
	AttributeMessagingPayloadCompressedSize = "messaging.message_payload_compressed_size_bytes"
	AttributeMessagingPayloadSize           = "messaging.message_payload_size_bytes"
	AttributeMessagingProtocol              = "messaging.protocol"
	AttributeMessagingProtocolVersion       = "messaging.protocol_version"
	AttributeMessagingSystem                = "messaging.system"
	AttributeMessagingTempDestination       = "messaging.temp_destination"
	AttributeMessagingURL                   = "messaging.url"
)

// OpenTelemetry Semantic Convention attribute names for exceptions
// See: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/exceptions.md
const (
	AttributeExceptionEventName  = "exception"
	AttributeExceptionMessage    = "exception.message"
	AttributeExceptionStacktrace = "exception.stacktrace"
	AttributeExceptionType       = "exception.type"
)
<commit_msg>Add k8s.node semantic conventions (#2425)<commit_after>// Copyright The OpenTelemetry Authors
//
// 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
//
//       http://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.

package conventions

// OpenTelemetry Semantic Convention values for Resource attribute names.
// See: https://github.com/open-telemetry/opentelemetry-specification/tree/main/specification/resource/semantic_conventions/README.md
const (
	AttributeCloudAccount               = "cloud.account.id"
	AttributeCloudProvider              = "cloud.provider"
	AttributeCloudRegion                = "cloud.region"
	AttributeCloudZone                  = "cloud.zone"
	AttributeCloudInfrastructureService = "cloud.infrastructure_service"
	AttributeContainerID                = "container.id"
	AttributeContainerImage             = "container.image.name"
	AttributeContainerName              = "container.name"
	AttributeContainerTag               = "container.image.tag"
	AttributeDeploymentEnvironment      = "deployment.environment"
	AttributeFaasID                     = "faas.id"
	AttributeFaasInstance               = "faas.instance"
	AttributeFaasName                   = "faas.name"
	AttributeFaasVersion                = "faas.version"
	AttributeHostID                     = "host.id"
	AttributeHostImageID                = "host.image.id"
	AttributeHostImageName              = "host.image.name"
	AttributeHostImageVersion           = "host.image.version"
	AttributeHostName                   = "host.name"
	AttributeHostType                   = "host.type"
	AttributeK8sCluster                 = "k8s.cluster.name"
	AttributeK8sContainer               = "k8s.container.name"
	AttributeK8sCronJob                 = "k8s.cronjob.name"
	AttributeK8sCronJobUID              = "k8s.cronjob.uid"
	AttributeK8sDaemonSet               = "k8s.daemonset.name"
	AttributeK8sDaemonSetUID            = "k8s.daemonset.uid"
	AttributeK8sDeployment              = "k8s.deployment.name"
	AttributeK8sDeploymentUID           = "k8s.deployment.uid"
	AttributeK8sJob                     = "k8s.job.name"
	AttributeK8sJobUID                  = "k8s.job.uid"
	AttributeK8sNamespace               = "k8s.namespace.name"
	AttributeK8sNodeName                = "k8s.node.name"
	AttributeK8sNodeUID                 = "k8s.node.uid"
	AttributeK8sPod                     = "k8s.pod.name"
	AttributeK8sPodUID                  = "k8s.pod.uid"
	AttributeK8sReplicaSet              = "k8s.replicaset.name"
	AttributeK8sReplicaSetUID           = "k8s.replicaset.uid"
	AttributeK8sStatefulSet             = "k8s.statefulset.name"
	AttributeK8sStatefulSetUID          = "k8s.statefulset.uid"
	AttributeOSType                     = "os.type"
	AttributeOSDescription              = "os.description"
	AttributeProcessCommand             = "process.command"
	AttributeProcessCommandLine         = "process.command_line"
	AttributeProcessExecutableName      = "process.executable.name"
	AttributeProcessExecutablePath      = "process.executable.path"
	AttributeProcessID                  = "process.pid"
	AttributeProcessOwner               = "process.owner"
	AttributeServiceInstance            = "service.instance.id"
	AttributeServiceName                = "service.name"
	AttributeServiceNamespace           = "service.namespace"
	AttributeServiceVersion             = "service.version"
	AttributeTelemetryAutoVersion       = "telemetry.auto.version"
	AttributeTelemetrySDKLanguage       = "telemetry.sdk.language"
	AttributeTelemetrySDKName           = "telemetry.sdk.name"
	AttributeTelemetrySDKVersion        = "telemetry.sdk.version"
)

// OpenTelemetry Semantic Convention values for Resource attribute "telemetry.sdk.language" values.
// See: https://github.com/open-telemetry/opentelemetry-specification/tree/main/specification/resource/semantic_conventions/README.md
const (
	AttributeSDKLangValueCPP    = "cpp"
	AttributeSDKLangValueDotNET = "dotnet"
	AttributeSDKLangValueErlang = "erlang"
	AttributeSDKLangValueGo     = "go"
	AttributeSDKLangValueJava   = "java"
	AttributeSDKLangValueNodeJS = "nodejs"
	AttributeSDKLangValuePHP    = "php"
	AttributeSDKLangValuePython = "python"
	AttributeSDKLangValueRuby   = "ruby"
	AttributeSDKLangValueWebJS  = "webjs"
)

// OpenTelemetry Semantic Convention values for Resource attribute "cloud.provider" values.
// See: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/semantic_conventions/cloud.md
const (
	AttributeCloudProviderAWS   = "aws"
	AttributeCloudProviderAzure = "azure"
	AttributeCloudProviderGCP   = "gcp"
)

// OpenTelemetry Semantic Convention values for Resource attribute "cloud.infrastructure_service" values.
// See: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/semantic_conventions/cloud.md
const (
	AttributeCloudProviderAWSEC2                  = "aws_ec2"
	AttributeCloudProviderAWSECS                  = "aws_ecs"
	AttributeCloudProviderAWSEKS                  = "aws_eks"
	AttributeCloudProviderAWSLambda               = "aws_lambda"
	AttributeCloudProviderAWSElasticBeanstalk     = "aws_elastic_beanstalk"
	AttributeCloudProviderAzureVM                 = "azure_vm"
	AttributeCloudProviderAzureContainerInstances = "azure_container_instances"
	AttributeCloudProviderAzureAKS                = "azure_aks"
	AttributeCloudProviderAzureFunctions          = "azure_functions"
	AttributeCloudProviderAzureAppService         = "azure_app_service"
	AttributeCloudProviderGCPComputeEngine        = "gcp_compute_engine"
	AttributeCloudProviderGCPCloudRun             = "gcp_cloud_run"
	AttributeCloudProviderGCPGKE                  = "gcp_gke"
	AttributeCloudProviderGCPCloudFunctions       = "gcp_cloud_functions"
	AttributeCloudProviderGCPAppEngine            = "gcp_app_engine"
)

// GetResourceSemanticConventionAttributeNames a slice with all the Resource Semantic Conventions attribute names.
func GetResourceSemanticConventionAttributeNames() []string {
	return []string{
		AttributeCloudAccount,
		AttributeCloudProvider,
		AttributeCloudRegion,
		AttributeCloudZone,
		AttributeCloudInfrastructureService,
		AttributeContainerID,
		AttributeContainerImage,
		AttributeContainerName,
		AttributeContainerTag,
		AttributeDeploymentEnvironment,
		AttributeFaasID,
		AttributeFaasInstance,
		AttributeFaasName,
		AttributeFaasVersion,
		AttributeHostID,
		AttributeHostImageID,
		AttributeHostImageName,
		AttributeHostImageVersion,
		AttributeHostName,
		AttributeHostType,
		AttributeK8sCluster,
		AttributeK8sContainer,
		AttributeK8sCronJob,
		AttributeK8sCronJobUID,
		AttributeK8sDaemonSet,
		AttributeK8sDaemonSetUID,
		AttributeK8sDeployment,
		AttributeK8sDeploymentUID,
		AttributeK8sJob,
		AttributeK8sJobUID,
		AttributeK8sNamespace,
		AttributeK8sNodeName,
		AttributeK8sNodeUID,
		AttributeK8sPod,
		AttributeK8sPodUID,
		AttributeK8sReplicaSet,
		AttributeK8sReplicaSetUID,
		AttributeK8sStatefulSet,
		AttributeK8sStatefulSetUID,
		AttributeOSType,
		AttributeOSDescription,
		AttributeProcessCommand,
		AttributeProcessCommandLine,
		AttributeProcessExecutableName,
		AttributeProcessExecutablePath,
		AttributeProcessID,
		AttributeProcessOwner,
		AttributeServiceInstance,
		AttributeServiceName,
		AttributeServiceNamespace,
		AttributeServiceVersion,
		AttributeTelemetryAutoVersion,
		AttributeTelemetrySDKLanguage,
		AttributeTelemetrySDKName,
		AttributeTelemetrySDKVersion,
	}
}

// OpenTelemetry Semantic Convention values for general Span attribute names.
// See: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/span-general.md
const (
	AttributeComponent    = "component"
	AttributeEnduserID    = "enduser.id"
	AttributeEnduserRole  = "enduser.role"
	AttributeEnduserScope = "enduser.scope"
	AttributeNetHostIP    = "net.host.ip"
	AttributeNetHostName  = "net.host.name"
	AttributeNetHostPort  = "net.host.port"
	AttributeNetPeerIP    = "net.peer.ip"
	AttributeNetPeerName  = "net.peer.name"
	AttributeNetPeerPort  = "net.peer.port"
	AttributeNetTransport = "net.transport"
	AttributePeerService  = "peer.service"
)

// OpenTelemetry Semantic Convention values for component attribute values.
// Possibly being removed due to issue #336
const (
	ComponentTypeHTTP = "http"
	ComponentTypeGRPC = "grpc"
)

// OpenTelemetry Semantic Convention attribute names for HTTP related attributes
// See: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md
const (
	AttributeHTTPClientIP                          = "http.client_ip"
	AttributeHTTPFlavor                            = "http.flavor"
	AttributeHTTPHost                              = "http.host"
	AttributeHTTPHostName                          = "host.name"
	AttributeHTTPHostPort                          = "host.port"
	AttributeHTTPMethod                            = "http.method"
	AttributeHTTPRequestContentLength              = "http.request_content_length"
	AttributeHTTPRequestContentLengthUncompressed  = "http.request_content_length_uncompressed"
	AttributeHTTPResponseContentLength             = "http.response_content_length"
	AttributeHTTPResponseContentLengthUncompressed = "http.response_content_length_uncompressed"
	AttributeHTTPRoute                             = "http.route"
	AttributeHTTPScheme                            = "http.scheme"
	AttributeHTTPServerName                        = "http.server_name"
	AttributeHTTPStatusCode                        = "http.status_code"
	AttributeHTTPStatusText                        = "http.status_text"
	AttributeHTTPTarget                            = "http.target"
	AttributeHTTPURL                               = "http.url"
	AttributeHTTPUserAgent                         = "http.user_agent"
)

// OpenTelemetry Semantic Convention attribute names for database related attributes
// See: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/database.md
const (
	AttributeDBConnectionString = "db.connection_string"

	AttributeDBCassandraKeyspace   = "db.cassandra.keyspace"
	AttributeDBHBaseNamespace      = "db.hbase.namespace"
	AttributeDBJDBCDriverClassname = "db.jdbc.driver_classname"
	AttributeDBMongoDBCollection   = "db.mongodb.collection"
	AttributeDBMsSQLInstanceName   = "db.mssql.instance_name"

	AttributeDBName               = "db.name"
	AttributeDBOperation          = "db.operation"
	AttributeDBRedisDatabaseIndex = "db.redis.database_index"
	AttributeDBStatement          = "db.statement"
	AttributeDBSystem             = "db.system"
	AttributeDBUser               = "db.user"
)

// OpenTelemetry Semantic Convention attribute names for gRPC related attributes
// See: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/rpc.md
const (
	AttributeMessageCompressedSize   = "message.compressed_size"
	AttributeMessageID               = "message.id"
	AttributeMessageType             = "message.type"
	AttributeMessageUncompressedSize = "message.uncompressed_size"
	AttributeRPCMethod               = "rpc.method"
	AttributeRPCService              = "rpc.service"
	AttributeRPCSystem               = "rpc.system"
	EventTypeMessage                 = "message"
	MessageTypeReceived              = "RECEIVED"
	MessageTypeSent                  = "SENT"
)

// OpenTelemetry Semantic Convention attribute names for FaaS related attributes
// See: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/faas.md
const (
	AttributeFaaSCron               = "faas.cron"
	AttributeFaaSDocumentCollection = "faas.document.collection"
	AttributeFaaSDocumentName       = "faas.document.name"
	AttributeFaaSDocumentOperation  = "faas.document.operation"
	AttributeFaaSDocumentTime       = "faas.document.time"
	AttributeFaaSExecution          = "faas.execution"
	AttributeFaaSTime               = "faas.time"
	AttributeFaaSTrigger            = "faas.trigger"
	FaaSTriggerDataSource           = "datasource"
	FaaSTriggerHTTP                 = "http"
	FaaSTriggerOther                = "other"
	FaaSTriggerPubSub               = "pubsub"
	FaaSTriggerTimer                = "timer"
)

// OpenTelemetry Semantic Convention attribute names for messaging system related attributes
// See: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/messaging.md
const (
	AttributeMessagingConversationID        = "messaging.conversation_id"
	AttributeMessagingDestination           = "messaging.destination"
	AttributeMessagingDestinationKind       = "messaging.destination_kind"
	AttributeMessagingMessageID             = "messaging.message_id"
	AttributeMessagingOperation             = "messaging.operation"
	AttributeMessagingPayloadCompressedSize = "messaging.message_payload_compressed_size_bytes"
	AttributeMessagingPayloadSize           = "messaging.message_payload_size_bytes"
	AttributeMessagingProtocol              = "messaging.protocol"
	AttributeMessagingProtocolVersion       = "messaging.protocol_version"
	AttributeMessagingSystem                = "messaging.system"
	AttributeMessagingTempDestination       = "messaging.temp_destination"
	AttributeMessagingURL                   = "messaging.url"
)

// OpenTelemetry Semantic Convention attribute names for exceptions
// See: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/exceptions.md
const (
	AttributeExceptionEventName  = "exception"
	AttributeExceptionMessage    = "exception.message"
	AttributeExceptionStacktrace = "exception.stacktrace"
	AttributeExceptionType       = "exception.type"
)
<|endoftext|>


<commit_before>// Copyright 2013-2014 Aaron Hopkins. All rights reserved.
// Use of this source code is governed by the GPL v2 license
// license that can be found in the LICENSE file.

package imager

import (
	"fmt"
	"github.com/gographics/imagick/imagick"
)

type Result struct {
	wand        *imagick.MagickWand
	img         *Imager
	Width       uint
	Height      uint
	Orientation Orientation
	shrank      bool
}

func (img *Imager) NewResult(width, height uint) (*Result, error) {
	result := &Result{
		Orientation: *img.Orientation,
		img:         img,
		wand:        imagick.NewMagickWand(),
	}

	if width > 0 && height > 0 {
		// Ask the jpeg decoder to pre-scale for us, down to something at least
		// as big as this.  This is often a huge performance gain.
		ow, oh := result.Orientation.Dimensions(width, height)
		s := fmt.Sprintf("%dx%d", ow, oh)
		if err := result.wand.SetOption("jpeg:size", s); err != nil {
			result.Close()
			return nil, err
		}
	}

	// Decompress the image into a pixel buffer, possibly pre-scaling first.
	if err := result.wand.ReadImageBlob(img.blob); err != nil {
		result.Close()
		return nil, err
	}

	if result.applyColorProfile() {
		// Make sure ImageMagick is aware that this is now sRGB.
		if err := result.wand.SetColorspace(imagick.COLORSPACE_SRGB); err != nil {
			result.Close()
			return nil, err
		}
	} else if result.wand.GetImageColorspace() != imagick.COLORSPACE_SRGB {
		// Switch to sRGB colorspace, the default for the web.
		if err := result.wand.TransformImageColorspace(imagick.COLORSPACE_SRGB); err != nil {
			result.Close()
			return nil, err
		}
	}

	// These may be smaller than img.Width and img.Height if JPEG decoder pre-scaled image.
	result.Width, result.Height = result.Orientation.Dimensions(result.wand.GetImageWidth(), result.wand.GetImageHeight())

	if result.Width < img.Width && result.Height < img.Height {
		result.shrank = true
	}

	return result, nil
}

func (result *Result) applyColorProfile() bool {
	icc := result.wand.GetImageProfile("icc")
	if icc == "" {
		return false // no color profile
	}

	if icc == sRGB_IEC61966_2_1_black_scaled {
		return true // already applied
	}

	// Apply sRGB IEC 61966 2.1 to this image.
	err := result.wand.ProfileImage("icc", []byte(sRGB_IEC61966_2_1_black_scaled))
	return err == nil // did we successfully apply?
}

func (result *Result) Resize(width, height uint) error {
	// Only use Lanczos if we are shrinking by more than 2.5%.
	filter := imagick.FILTER_TRIANGLE
	if width < result.Width-result.Width/40 && height < result.Height-result.Height/40 {
		filter = imagick.FILTER_LANCZOS2_SHARP
	}

	ow, oh := result.Orientation.Dimensions(width, height)
	if err := result.wand.ResizeImage(ow, oh, filter, 1); err != nil {
		return err
	}

	// Only change dimensions and/or set shrank flag on success.
	result.Width = width
	result.Height = height
	if filter == imagick.FILTER_LANCZOS2_SHARP {
		result.shrank = true
	}

	return nil
}

func (result *Result) Crop(width, height uint) error {
	if width > result.Width || height > result.Height {
		return TooBig
	}

	// Center horizontally
	x := (int(result.Width) - int(width) + 1) / 2
	// Assume faces are higher up vertically
	y := (int(result.Height) - int(height) + 1) / 4

	ow, oh, ox, oy := result.Orientation.Crop(width, height, x, y, result.Width, result.Height)
	if err := result.wand.CropImage(ow, oh, ox, oy); err != nil {
		return err
	}

	result.Width = width
	result.Height = height

	return nil
}

func (result *Result) Get() ([]byte, error) {
	// If the image shrunk, apply a light sharpening pass
	if result.shrank && result.img.Sharpen {
		if err := result.wand.UnsharpMaskImage(0, 0.8, 0.6, 0.05); err != nil {
			return nil, err
		}
	}

	// Only save at 8 bits per channel.
	if err := result.wand.SetImageDepth(8); err != nil {
		return nil, err
	}

	// Fix orientation.
	if err := result.Orientation.Fix(result.wand); err != nil {
		return nil, err
	}

	// Remove extraneous metadata and color profiles.
	if err := result.wand.StripImage(); err != nil {
		result.Close()
		return nil, err
	}

	// Output image format may differ from input format.
	if err := result.wand.SetImageFormat(result.img.OutputFormat); err != nil {
		return nil, err
	}

	switch result.img.OutputFormat {
	case "JPEG":
		if err := result.wand.SetImageCompressionQuality(result.img.JpegQuality); err != nil {
			return nil, err
		}

		// This creates "Progressive JPEGs", which are smaller.
		// Don't use for non-JPEG.
		if err := result.wand.SetInterlaceScheme(imagick.INTERLACE_LINE); err != nil {
			return nil, err
		}
	case "PNG":
		// Don't preserve data for fully-transparent pixels.
		if err := result.wand.SetImageAlphaChannel(imagick.ALPHA_CHANNEL_BACKGROUND); err != nil {
			return nil, err
		}

		// PNG quality: 95 = Gzip level=9, adaptive strategy=5
		if err := result.wand.SetImageCompressionQuality(95); err != nil {
			return nil, err
		}
	}

	// Run the format-specific compressor, return the byte slice.
	return result.wand.GetImageBlob(), nil
}

func (result *Result) Close() {
	// imagick.MagicWand will otherwise leak unless we wand.Destroy().
	result.wand.Destroy()

	*result = Result{}
}
<commit_msg>Reset virtual canvas information after loading PNGs.<commit_after>// Copyright 2013-2014 Aaron Hopkins. All rights reserved.
// Use of this source code is governed by the GPL v2 license
// license that can be found in the LICENSE file.

package imager

import (
	"fmt"
	"github.com/gographics/imagick/imagick"
)

type Result struct {
	wand        *imagick.MagickWand
	img         *Imager
	Width       uint
	Height      uint
	Orientation Orientation
	shrank      bool
}

func (img *Imager) NewResult(width, height uint) (*Result, error) {
	result := &Result{
		Orientation: *img.Orientation,
		img:         img,
		wand:        imagick.NewMagickWand(),
	}

	if width > 0 && height > 0 {
		// Ask the jpeg decoder to pre-scale for us, down to something at least
		// as big as this.  This is often a huge performance gain.
		ow, oh := result.Orientation.Dimensions(width, height)
		s := fmt.Sprintf("%dx%d", ow, oh)
		if err := result.wand.SetOption("jpeg:size", s); err != nil {
			result.Close()
			return nil, err
		}
	}

	// Decompress the image into a pixel buffer, possibly pre-scaling first.
	if err := result.wand.ReadImageBlob(img.blob); err != nil {
		result.Close()
		return nil, err
	}

	// Reset virtual canvas and position.
	if err := result.wand.ResetImagePage(""); err != nil {
		result.Close()
		return nil, err
	}

	if result.applyColorProfile() {
		// Make sure ImageMagick is aware that this is now sRGB.
		if err := result.wand.SetColorspace(imagick.COLORSPACE_SRGB); err != nil {
			result.Close()
			return nil, err
		}
	} else if result.wand.GetImageColorspace() != imagick.COLORSPACE_SRGB {
		// Switch to sRGB colorspace, the default for the web.
		if err := result.wand.TransformImageColorspace(imagick.COLORSPACE_SRGB); err != nil {
			result.Close()
			return nil, err
		}
	}

	// These may be smaller than img.Width and img.Height if JPEG decoder pre-scaled image.
	result.Width, result.Height = result.Orientation.Dimensions(result.wand.GetImageWidth(), result.wand.GetImageHeight())

	if result.Width < img.Width && result.Height < img.Height {
		result.shrank = true
	}

	return result, nil
}

func (result *Result) applyColorProfile() bool {
	icc := result.wand.GetImageProfile("icc")
	if icc == "" {
		return false // no color profile
	}

	if icc == sRGB_IEC61966_2_1_black_scaled {
		return true // already applied
	}

	// Apply sRGB IEC 61966 2.1 to this image.
	err := result.wand.ProfileImage("icc", []byte(sRGB_IEC61966_2_1_black_scaled))
	return err == nil // did we successfully apply?
}

func (result *Result) Resize(width, height uint) error {
	// Only use Lanczos if we are shrinking by more than 2.5%.
	filter := imagick.FILTER_TRIANGLE
	if width < result.Width-result.Width/40 && height < result.Height-result.Height/40 {
		filter = imagick.FILTER_LANCZOS2_SHARP
	}

	ow, oh := result.Orientation.Dimensions(width, height)
	if err := result.wand.ResizeImage(ow, oh, filter, 1); err != nil {
		return err
	}

	// Only change dimensions and/or set shrank flag on success.
	result.Width = width
	result.Height = height
	if filter == imagick.FILTER_LANCZOS2_SHARP {
		result.shrank = true
	}

	return nil
}

func (result *Result) Crop(width, height uint) error {
	if width > result.Width || height > result.Height {
		return TooBig
	}

	// Center horizontally
	x := (int(result.Width) - int(width) + 1) / 2
	// Assume faces are higher up vertically
	y := (int(result.Height) - int(height) + 1) / 4

	ow, oh, ox, oy := result.Orientation.Crop(width, height, x, y, result.Width, result.Height)
	if err := result.wand.CropImage(ow, oh, ox, oy); err != nil {
		return err
	}

	result.Width = width
	result.Height = height

	return nil
}

func (result *Result) Get() ([]byte, error) {
	// If the image shrunk, apply a light sharpening pass
	if result.shrank && result.img.Sharpen {
		if err := result.wand.UnsharpMaskImage(0, 0.8, 0.6, 0.05); err != nil {
			return nil, err
		}
	}

	// Only save at 8 bits per channel.
	if err := result.wand.SetImageDepth(8); err != nil {
		return nil, err
	}

	// Fix orientation.
	if err := result.Orientation.Fix(result.wand); err != nil {
		return nil, err
	}

	// Remove extraneous metadata and color profiles.
	if err := result.wand.StripImage(); err != nil {
		result.Close()
		return nil, err
	}

	// Output image format may differ from input format.
	if err := result.wand.SetImageFormat(result.img.OutputFormat); err != nil {
		return nil, err
	}

	switch result.img.OutputFormat {
	case "JPEG":
		if err := result.wand.SetImageCompressionQuality(result.img.JpegQuality); err != nil {
			return nil, err
		}

		// This creates "Progressive JPEGs", which are smaller.
		// Don't use for non-JPEG.
		if err := result.wand.SetInterlaceScheme(imagick.INTERLACE_LINE); err != nil {
			return nil, err
		}
	case "PNG":
		// Don't preserve data for fully-transparent pixels.
		if err := result.wand.SetImageAlphaChannel(imagick.ALPHA_CHANNEL_BACKGROUND); err != nil {
			return nil, err
		}

		// PNG quality: 95 = Gzip level=9, adaptive strategy=5
		if err := result.wand.SetImageCompressionQuality(95); err != nil {
			return nil, err
		}
	}

	// Run the format-specific compressor, return the byte slice.
	return result.wand.GetImageBlob(), nil
}

func (result *Result) Close() {
	// imagick.MagicWand will otherwise leak unless we wand.Destroy().
	result.wand.Destroy()

	*result = Result{}
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	ggio "github.com/gogo/protobuf/io"
	ggproto "github.com/gogo/protobuf/proto"
	mux "github.com/gorilla/mux"
	p2p_peer "github.com/ipfs/go-libp2p-peer"
	p2p_pstore "github.com/ipfs/go-libp2p-peerstore"
	multiaddr "github.com/jbenet/go-multiaddr"
	p2p_host "github.com/libp2p/go-libp2p/p2p/host"
	p2p_net "github.com/libp2p/go-libp2p/p2p/net"
	mc "github.com/mediachain/concat/mc"
	pb "github.com/mediachain/concat/proto"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"sync"
	"time"
)

type Node struct {
	mc.Identity
	host    p2p_host.Host
	dir     p2p_pstore.PeerInfo
	home    string
	mx      sync.Mutex
	stmt    map[string]*pb.Statement
	counter int
}

func (node *Node) pingHandler(s p2p_net.Stream) {
	defer s.Close()

	pid := s.Conn().RemotePeer()
	log.Printf("node/ping: new stream from %s", pid.Pretty())

	var ping pb.Ping
	var pong pb.Pong
	r := ggio.NewDelimitedReader(s, mc.MaxMessageSize)
	w := ggio.NewDelimitedWriter(s)

	for {
		err := r.ReadMsg(&ping)
		if err != nil {
			return
		}

		log.Printf("node/ping: ping from %s; ponging", pid.Pretty())

		err = w.WriteMsg(&pong)
		if err != nil {
			return
		}
	}
}

func (node *Node) registerPeer(addrs ...multiaddr.Multiaddr) {
	// directory failure is a fatality for now
	ctx := context.Background()

	err := node.host.Connect(ctx, node.dir)
	if err != nil {
		log.Printf("Failed to connect to directory")
		log.Fatal(err)
	}

	s, err := node.host.NewStream(ctx, node.dir.ID, "/mediachain/dir/register")
	if err != nil {
		log.Printf("Failed to open directory stream")
		log.Fatal(err)
	}
	defer s.Close()

	pinfo := p2p_pstore.PeerInfo{node.ID, addrs}
	var pbpi pb.PeerInfo
	mc.PBFromPeerInfo(&pbpi, pinfo)
	msg := pb.RegisterPeer{&pbpi}

	w := ggio.NewDelimitedWriter(s)
	for {
		log.Printf("Registering with directory")
		err = w.WriteMsg(&msg)
		if err != nil {
			log.Printf("Failed to register with directory")
			log.Fatal(err)
		}

		time.Sleep(5 * time.Minute)
	}
}

func (node *Node) httpId(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, node.Identity.Pretty())
}

func (node *Node) httpPing(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	peerId := vars["peerId"]
	pid, err := p2p_peer.IDB58Decode(peerId)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "Error: Bad id: %s\n", err.Error())
		return
	}

	err = node.doPing(r.Context(), pid)
	if err != nil {
		w.WriteHeader(http.StatusNotFound)
		fmt.Fprintf(w, "Error: %s\n", err.Error())
		return
	}

	fmt.Fprintf(w, "OK\n")
}

func (node *Node) doPing(ctx context.Context, pid p2p_peer.ID) error {
	pinfo, err := node.doLookup(ctx, pid)
	if err != nil {
		return err
	}

	err = node.host.Connect(ctx, pinfo)
	if err != nil {
		return err
	}

	s, err := node.host.NewStream(ctx, pinfo.ID, "/mediachain/node/ping")
	if err != nil {
		return err
	}
	defer s.Close()

	var ping pb.Ping
	w := ggio.NewDelimitedWriter(s)
	err = w.WriteMsg(&ping)
	if err != nil {
		return err
	}

	var pong pb.Pong
	r := ggio.NewDelimitedReader(s, mc.MaxMessageSize)
	err = r.ReadMsg(&pong)

	return err
}

var UnknownPeer = errors.New("Unknown peer")

func (node *Node) doLookup(ctx context.Context, pid p2p_peer.ID) (empty p2p_pstore.PeerInfo, err error) {
	s, err := node.host.NewStream(ctx, node.dir.ID, "/mediachain/dir/lookup")
	if err != nil {
		return empty, err
	}
	defer s.Close()

	req := pb.LookupPeerRequest{string(pid)}
	w := ggio.NewDelimitedWriter(s)
	err = w.WriteMsg(&req)
	if err != nil {
		return empty, err
	}

	var resp pb.LookupPeerResponse
	r := ggio.NewDelimitedReader(s, mc.MaxMessageSize)
	err = r.ReadMsg(&resp)
	if err != nil {
		return empty, err
	}

	if resp.Peer == nil {
		return empty, UnknownPeer
	}

	pinfo, err := mc.PBToPeerInfo(resp.Peer)
	if err != nil {
		return empty, err
	}

	return pinfo, nil
}

func (node *Node) httpPublish(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	ns := vars["namespace"]

	rbody, err := ioutil.ReadAll(r.Body)
	if err != nil {
		log.Printf("http/publish: Error reading request body: %s", err.Error())
		return
	}

	// just simple statements for now
	sbody := new(pb.SimpleStatement)
	err = json.Unmarshal(rbody, sbody)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "Error: %s\n", err.Error())
		return
	}

	sid, err := node.doPublish(ns, sbody)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "Error: %s\n", err.Error())
		return
	}

	fmt.Fprintln(w, sid)
}

var BadStatementBody = errors.New("Unrecognized statement body")

func (node *Node) doPublish(ns string, body interface{}) (string, error) {
	stmt := new(pb.Statement)
	pid := node.ID.Pretty()
	ts := time.Now().Unix()
	counter := node.stmtCounter()
	stmt.Id = fmt.Sprintf("%s:%d:%d", pid, ts, counter)
	stmt.Publisher = pid // this should be the pubkey when we have ECC keys
	stmt.Namespace = ns
	stmt.Timestamp = ts
	switch body := body.(type) {
	case *pb.SimpleStatement:
		stmt.Body = &pb.Statement_Simple{body}

	case *pb.CompoundStatement:
		stmt.Body = &pb.Statement_Compound{body}

	case *pb.EnvelopeStatement:
		stmt.Body = &pb.Statement_Envelope{body}

	case *pb.ArchiveStatement:
		stmt.Body = &pb.Statement_Archive{body}

	default:
		return "", BadStatementBody
	}
	// only sign it with shiny ECC keys, don't bother with RSA
	log.Printf("Publish statement %s", stmt.Id)

	node.mx.Lock()
	node.stmt[stmt.Id] = stmt
	node.mx.Unlock()

	err := node.saveStatement(stmt)
	if err != nil {
		log.Printf("Warning: Failed to save statement: %s", err.Error())
	}

	return stmt.Id, nil
}

func (node *Node) stmtCounter() int {
	node.mx.Lock()
	counter := node.counter
	node.counter++
	node.mx.Unlock()
	return counter
}

func (node *Node) httpStatement(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id := vars["statementId"]

	var stmt *pb.Statement
	node.mx.Lock()
	stmt = node.stmt[id]
	node.mx.Unlock()

	if stmt == nil {
		w.WriteHeader(http.StatusNotFound)
		fmt.Fprintf(w, "No such statement\n")
		return
	}

	err := json.NewEncoder(w).Encode(stmt)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintf(w, "Error: %s\n", err.Error())
		return
	}
}

func (node *Node) saveStatement(stmt *pb.Statement) error {
	spath := path.Join(node.home, "stmt", stmt.Id)

	bytes, err := ggproto.Marshal(stmt)
	if err != nil {
		return err
	}

	log.Printf("Writing statement %s", spath)
	return ioutil.WriteFile(spath, bytes, 0644)
}

func (node *Node) loadStatements() {
	sdir := path.Join(node.home, "stmt")
	err := filepath.Walk(sdir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if info.IsDir() {
			return nil
		}

		log.Printf("Loading statement %s", path)
		bytes, err := ioutil.ReadFile(path)
		if err != nil {
			return err
		}

		stmt := new(pb.Statement)
		err = ggproto.Unmarshal(bytes, stmt)
		if err != nil {
			return err
		}

		node.stmt[stmt.Id] = stmt
		return nil
	})

	if err != nil {
		log.Fatal(err)
	}
}

func main() {
	pport := flag.Int("l", 9001, "Peer listen port")
	cport := flag.Int("c", 9002, "Peer control interface port [http]")
	home := flag.String("d", "/tmp/mcnode", "Node home")
	flag.Parse()

	if len(flag.Args()) != 1 {
		fmt.Fprintf(os.Stderr, "Usage: %s [options ...] directory\nOptions:\n", os.Args[0])
		flag.PrintDefaults()
		os.Exit(1)
	}

	addr, err := mc.ParseAddress(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", *pport))
	if err != nil {
		log.Fatal(err)
	}

	dir, err := mc.ParseHandle(flag.Arg(0))
	if err != nil {
		log.Fatal(err)
	}

	err = os.MkdirAll(*home, 0755)
	if err != nil {
		log.Fatal(err)
	}

	err = os.MkdirAll(path.Join(*home, "stmt"), 0755)
	if err != nil {
		log.Fatal(err)
	}

	id, err := mc.NodeIdentity(*home)
	if err != nil {
		log.Fatal(err)
	}

	host, err := mc.NewHost(id, addr)
	if err != nil {
		log.Fatal(err)
	}

	node := &Node{Identity: id, host: host, dir: dir, home: *home, stmt: make(map[string]*pb.Statement)}

	node.loadStatements()

	host.SetStreamHandler("/mediachain/node/ping", node.pingHandler)
	go node.registerPeer(addr)

	log.Printf("I am %s/%s", addr, id.Pretty())

	haddr := fmt.Sprintf("127.0.0.1:%d", *cport)
	router := mux.NewRouter().StrictSlash(true)
	router.HandleFunc("/id", node.httpId)
	router.HandleFunc("/ping/{peerId}", node.httpPing)
	router.HandleFunc("/publish/{namespace}", node.httpPublish)
	router.HandleFunc("/stmt/{statementId}", node.httpStatement)

	log.Printf("Serving client interface at %s", haddr)
	err = http.ListenAndServe(haddr, router)
	if err != nil {
		log.Fatal(err)
	}

	select {}
}
<commit_msg>mcnode: query api<commit_after>package main

import (
	"context"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	ggio "github.com/gogo/protobuf/io"
	ggproto "github.com/gogo/protobuf/proto"
	mux "github.com/gorilla/mux"
	p2p_peer "github.com/ipfs/go-libp2p-peer"
	p2p_pstore "github.com/ipfs/go-libp2p-peerstore"
	multiaddr "github.com/jbenet/go-multiaddr"
	p2p_host "github.com/libp2p/go-libp2p/p2p/host"
	p2p_net "github.com/libp2p/go-libp2p/p2p/net"
	mc "github.com/mediachain/concat/mc"
	mcq "github.com/mediachain/concat/mc/query"
	pb "github.com/mediachain/concat/proto"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"sync"
	"time"
)

type Node struct {
	mc.Identity
	host    p2p_host.Host
	dir     p2p_pstore.PeerInfo
	home    string
	mx      sync.Mutex
	stmt    map[string]*pb.Statement
	counter int
}

func (node *Node) pingHandler(s p2p_net.Stream) {
	defer s.Close()

	pid := s.Conn().RemotePeer()
	log.Printf("node/ping: new stream from %s", pid.Pretty())

	var ping pb.Ping
	var pong pb.Pong
	r := ggio.NewDelimitedReader(s, mc.MaxMessageSize)
	w := ggio.NewDelimitedWriter(s)

	for {
		err := r.ReadMsg(&ping)
		if err != nil {
			return
		}

		log.Printf("node/ping: ping from %s; ponging", pid.Pretty())

		err = w.WriteMsg(&pong)
		if err != nil {
			return
		}
	}
}

func (node *Node) registerPeer(addrs ...multiaddr.Multiaddr) {
	// directory failure is a fatality for now
	ctx := context.Background()

	err := node.host.Connect(ctx, node.dir)
	if err != nil {
		log.Printf("Failed to connect to directory")
		log.Fatal(err)
	}

	s, err := node.host.NewStream(ctx, node.dir.ID, "/mediachain/dir/register")
	if err != nil {
		log.Printf("Failed to open directory stream")
		log.Fatal(err)
	}
	defer s.Close()

	pinfo := p2p_pstore.PeerInfo{node.ID, addrs}
	var pbpi pb.PeerInfo
	mc.PBFromPeerInfo(&pbpi, pinfo)
	msg := pb.RegisterPeer{&pbpi}

	w := ggio.NewDelimitedWriter(s)
	for {
		log.Printf("Registering with directory")
		err = w.WriteMsg(&msg)
		if err != nil {
			log.Printf("Failed to register with directory")
			log.Fatal(err)
		}

		time.Sleep(5 * time.Minute)
	}
}

func (node *Node) httpId(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, node.Identity.Pretty())
}

func (node *Node) httpPing(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	peerId := vars["peerId"]
	pid, err := p2p_peer.IDB58Decode(peerId)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "Error: Bad id: %s\n", err.Error())
		return
	}

	err = node.doPing(r.Context(), pid)
	if err != nil {
		w.WriteHeader(http.StatusNotFound)
		fmt.Fprintf(w, "Error: %s\n", err.Error())
		return
	}

	fmt.Fprintf(w, "OK\n")
}

func (node *Node) doPing(ctx context.Context, pid p2p_peer.ID) error {
	pinfo, err := node.doLookup(ctx, pid)
	if err != nil {
		return err
	}

	err = node.host.Connect(ctx, pinfo)
	if err != nil {
		return err
	}

	s, err := node.host.NewStream(ctx, pinfo.ID, "/mediachain/node/ping")
	if err != nil {
		return err
	}
	defer s.Close()

	var ping pb.Ping
	w := ggio.NewDelimitedWriter(s)
	err = w.WriteMsg(&ping)
	if err != nil {
		return err
	}

	var pong pb.Pong
	r := ggio.NewDelimitedReader(s, mc.MaxMessageSize)
	err = r.ReadMsg(&pong)

	return err
}

var UnknownPeer = errors.New("Unknown peer")

func (node *Node) doLookup(ctx context.Context, pid p2p_peer.ID) (empty p2p_pstore.PeerInfo, err error) {
	s, err := node.host.NewStream(ctx, node.dir.ID, "/mediachain/dir/lookup")
	if err != nil {
		return empty, err
	}
	defer s.Close()

	req := pb.LookupPeerRequest{string(pid)}
	w := ggio.NewDelimitedWriter(s)
	err = w.WriteMsg(&req)
	if err != nil {
		return empty, err
	}

	var resp pb.LookupPeerResponse
	r := ggio.NewDelimitedReader(s, mc.MaxMessageSize)
	err = r.ReadMsg(&resp)
	if err != nil {
		return empty, err
	}

	if resp.Peer == nil {
		return empty, UnknownPeer
	}

	pinfo, err := mc.PBToPeerInfo(resp.Peer)
	if err != nil {
		return empty, err
	}

	return pinfo, nil
}

func (node *Node) httpPublish(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	ns := vars["namespace"]

	rbody, err := ioutil.ReadAll(r.Body)
	if err != nil {
		log.Printf("http/publish: Error reading request body: %s", err.Error())
		return
	}

	// just simple statements for now
	sbody := new(pb.SimpleStatement)
	err = json.Unmarshal(rbody, sbody)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "Error: %s\n", err.Error())
		return
	}

	sid, err := node.doPublish(ns, sbody)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "Error: %s\n", err.Error())
		return
	}

	fmt.Fprintln(w, sid)
}

var BadStatementBody = errors.New("Unrecognized statement body")

func (node *Node) doPublish(ns string, body interface{}) (string, error) {
	stmt := new(pb.Statement)
	pid := node.ID.Pretty()
	ts := time.Now().Unix()
	counter := node.stmtCounter()
	stmt.Id = fmt.Sprintf("%s:%d:%d", pid, ts, counter)
	stmt.Publisher = pid // this should be the pubkey when we have ECC keys
	stmt.Namespace = ns
	stmt.Timestamp = ts
	switch body := body.(type) {
	case *pb.SimpleStatement:
		stmt.Body = &pb.Statement_Simple{body}

	case *pb.CompoundStatement:
		stmt.Body = &pb.Statement_Compound{body}

	case *pb.EnvelopeStatement:
		stmt.Body = &pb.Statement_Envelope{body}

	case *pb.ArchiveStatement:
		stmt.Body = &pb.Statement_Archive{body}

	default:
		return "", BadStatementBody
	}
	// only sign it with shiny ECC keys, don't bother with RSA
	log.Printf("Publish statement %s", stmt.Id)

	node.mx.Lock()
	node.stmt[stmt.Id] = stmt
	node.mx.Unlock()

	err := node.saveStatement(stmt)
	if err != nil {
		log.Printf("Warning: Failed to save statement: %s", err.Error())
	}

	return stmt.Id, nil
}

func (node *Node) stmtCounter() int {
	node.mx.Lock()
	counter := node.counter
	node.counter++
	node.mx.Unlock()
	return counter
}

func (node *Node) httpStatement(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id := vars["statementId"]

	var stmt *pb.Statement
	node.mx.Lock()
	stmt = node.stmt[id]
	node.mx.Unlock()

	if stmt == nil {
		w.WriteHeader(http.StatusNotFound)
		fmt.Fprintf(w, "No such statement\n")
		return
	}

	err := json.NewEncoder(w).Encode(stmt)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintf(w, "Error: %s\n", err.Error())
		return
	}
}

func (node *Node) httpQuery(w http.ResponseWriter, r *http.Request) {
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		log.Printf("http/query: Error reading request body: %s", err.Error())
		return
	}

	q, err := mcq.ParseQuery(string(body))
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "Error: %s\n", err.Error())
		return
	}

	res, err := node.doQuery(q)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintf(w, "Error: %s\n", err.Error())
		return
	}

	err = json.NewEncoder(w).Encode(res)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintf(w, "Error: %s\n", err.Error())
		return
	}
}

func (node *Node) doQuery(q *mcq.Query) ([]interface{}, error) {
	var stmts []*pb.Statement

	node.mx.Lock()
	stmts = make([]*pb.Statement, len(node.stmt))
	x := 0
	for _, stmt := range node.stmt {
		stmts[x] = stmt
		x++
	}
	node.mx.Unlock()

	return mcq.EvalQuery(q, stmts)
}

func (node *Node) saveStatement(stmt *pb.Statement) error {
	spath := path.Join(node.home, "stmt", stmt.Id)

	bytes, err := ggproto.Marshal(stmt)
	if err != nil {
		return err
	}

	log.Printf("Writing statement %s", spath)
	return ioutil.WriteFile(spath, bytes, 0644)
}

func (node *Node) loadStatements() {
	sdir := path.Join(node.home, "stmt")
	err := filepath.Walk(sdir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if info.IsDir() {
			return nil
		}

		log.Printf("Loading statement %s", path)
		bytes, err := ioutil.ReadFile(path)
		if err != nil {
			return err
		}

		stmt := new(pb.Statement)
		err = ggproto.Unmarshal(bytes, stmt)
		if err != nil {
			return err
		}

		node.stmt[stmt.Id] = stmt
		return nil
	})

	if err != nil {
		log.Fatal(err)
	}
}

func main() {
	pport := flag.Int("l", 9001, "Peer listen port")
	cport := flag.Int("c", 9002, "Peer control interface port [http]")
	home := flag.String("d", "/tmp/mcnode", "Node home")
	flag.Parse()

	if len(flag.Args()) != 1 {
		fmt.Fprintf(os.Stderr, "Usage: %s [options ...] directory\nOptions:\n", os.Args[0])
		flag.PrintDefaults()
		os.Exit(1)
	}

	addr, err := mc.ParseAddress(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", *pport))
	if err != nil {
		log.Fatal(err)
	}

	dir, err := mc.ParseHandle(flag.Arg(0))
	if err != nil {
		log.Fatal(err)
	}

	err = os.MkdirAll(*home, 0755)
	if err != nil {
		log.Fatal(err)
	}

	err = os.MkdirAll(path.Join(*home, "stmt"), 0755)
	if err != nil {
		log.Fatal(err)
	}

	id, err := mc.NodeIdentity(*home)
	if err != nil {
		log.Fatal(err)
	}

	host, err := mc.NewHost(id, addr)
	if err != nil {
		log.Fatal(err)
	}

	node := &Node{Identity: id, host: host, dir: dir, home: *home, stmt: make(map[string]*pb.Statement)}

	node.loadStatements()

	host.SetStreamHandler("/mediachain/node/ping", node.pingHandler)
	go node.registerPeer(addr)

	log.Printf("I am %s/%s", addr, id.Pretty())

	haddr := fmt.Sprintf("127.0.0.1:%d", *cport)
	router := mux.NewRouter().StrictSlash(true)
	router.HandleFunc("/id", node.httpId)
	router.HandleFunc("/ping/{peerId}", node.httpPing)
	router.HandleFunc("/publish/{namespace}", node.httpPublish)
	router.HandleFunc("/stmt/{statementId}", node.httpStatement)
	router.HandleFunc("/query", node.httpQuery)

	log.Printf("Serving client interface at %s", haddr)
	err = http.ListenAndServe(haddr, router)
	if err != nil {
		log.Fatal(err)
	}

	select {}
}
<|endoftext|>