<commit_before>package commands

import (
	"encoding/json"
	"fmt"
	"io/ioutil"

	"github.com/mesosphere/dcos-commons/cli/client"
	"github.com/mesosphere/dcos-commons/cli/config"
	"gopkg.in/alecthomas/kingpin.v3-unstable"
)

type describeHandler struct{}

type describeRequest struct {
	AppID string `json:"appId"`
}

func checkError(err error, responseBytes []byte) {
	if err != nil {
		reportErrorAndExit(err, responseBytes)
	}
}

func reportErrorAndExit(err error, responseBytes []byte) {
	client.PrintMessage("Failed to unmarshal response. Error: %s", err)
	client.PrintMessage("Original data follows:")
	client.PrintMessageAndExit(string(responseBytes))
}

func describe() {
	// TODO: figure out KingPin's error handling
	requestContent, err := json.Marshal(describeRequest{config.ServiceName})
	if err != nil {
		client.PrintMessageAndExit(err.Error())
	}
	responseBytes, err := client.HTTPCosmosPostJSON("describe", string(requestContent))
	if err != nil {
		client.PrintMessageAndExit(err.Error())
	}
	// This attempts to retrieve resolvedOptions from the response. This field is only provided by
	// Cosmos running on Enterprise DC/OS 1.10 clusters or later.
	resolvedOptionsBytes, err := client.GetValueFromJSONResponse(responseBytes, "resolvedOptions")
	checkError(err, responseBytes)
	if resolvedOptionsBytes != nil {
		client.PrintJSONBytes(resolvedOptionsBytes)
	} else {
		client.PrintMessage("Package configuration is not available for service %s.", config.ServiceName)
		client.PrintMessage("dcos %s %s is only available for packages installed with Enterprise DC/OS 1.10 or newer.", config.ModuleName, config.Command)
	}
}

func (cmd *describeHandler) handleDescribe(a *kingpin.Application, e *kingpin.ParseElement, c *kingpin.ParseContext) error {
	config.Command = c.SelectedCommand.FullCommand()
	describe()
	return nil
}

// HandleDescribe adds the describe subcommand to the passed in kingpin.Application.
func HandleDescribe(app *kingpin.Application) {
	cmd := &describeHandler{}
	app.Command("describe", "View the package configuration for this DC/OS service").Action(cmd.handleDescribe)
}

type updateHandler struct {
	UpdateName     string
	OptionsFile    string
	PackageVersion string
	ViewStatus     bool
}

type updateRequest struct {
	AppID          string                 `json:"appId"`
	PackageVersion string                 `json:"packageVersion,omitempty"`
	OptionsJSON    map[string]interface{} `json:"options,omitempty"`
}

func printPackageVersions() {
	// TODO: figure out KingPin's error handling
	requestContent, _ := json.Marshal(describeRequest{config.ServiceName})
	responseBytes, err := client.HTTPCosmosPostJSON("describe", string(requestContent))
	if err != nil {
		client.PrintMessageAndExit(err.Error())
	}
	packageBytes, err := client.GetValueFromJSONResponse(responseBytes, "package")
	checkError(err, responseBytes)
	currentVersionBytes, err := client.GetValueFromJSONResponse(packageBytes, "version")
	checkError(err, responseBytes)
	downgradeVersionsBytes, err := client.GetValueFromJSONResponse(responseBytes, "downgradesTo")
	checkError(err, responseBytes)
	downgradeVersions, err := client.JSONBytesToArray(downgradeVersionsBytes)
	checkError(err, responseBytes)
	upgradeVersionsBytes, err := client.GetValueFromJSONResponse(responseBytes, "upgradesTo")
	checkError(err, responseBytes)
	updateVersions, err := client.JSONBytesToArray(upgradeVersionsBytes)
	checkError(err, responseBytes)
	client.PrintMessage("Current package version is: %s", currentVersionBytes)
	if len(downgradeVersions) > 0 {
		client.PrintMessage("Package can be downgraded to: %s", client.PrettyPrintSlice(downgradeVersions))
	} else {
		client.PrintMessage("No valid package downgrade versions.")
	}
	if len(updateVersions) > 0 {
		client.PrintMessage("Package can be upgraded to: %s", client.PrettyPrintSlice(updateVersions))
	} else {
		client.PrintMessage("No valid package upgrade versions.")
	}
}

func (cmd *updateHandler) ViewPackageVersions(a *kingpin.Application, e *kingpin.ParseElement, c *kingpin.ParseContext) error {
	config.Command = c.SelectedCommand.FullCommand()
	printPackageVersions()
	return nil
}

func readJSONFile(filename string) (map[string]interface{}, error) {
	fileBytes, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	return client.UnmarshalJSON(fileBytes)
}

func parseUpdateResponse(responseBytes []byte) (string, error) {
	responseJSON, err := client.UnmarshalJSON(responseBytes)
	if err != nil {
		return "", err
	}
	return string(responseJSON["marathonDeploymentId"].(string)), nil
}

func doUpdate(optionsFile, packageVersion string) {
	// TODO: figure out KingPin's error handling
	request := updateRequest{AppID: config.ServiceName}
	if len(packageVersion) == 0 && len(optionsFile) == 0 {
		client.PrintMessage("Either --options and/or --package-version must be specified. See --help.")
		return<commit_msg>[INFINITY-1912] Add dcos-commons cli support for update hash merge semantics (#1310)<commit_after>package commands

import (
	"encoding/json"
	"fmt"
	"io/ioutil"

	"github.com/mesosphere/dcos-commons/cli/client"
	"github.com/mesosphere/dcos-commons/cli/config"
	"gopkg.in/alecthomas/kingpin.v3-unstable"
)

type describeHandler struct{}

type describeRequest struct {
	AppID string `json:"appId"`
}

func checkError(err error, responseBytes []byte) {
	if err != nil {
		reportErrorAndExit(err, responseBytes)
	}
}

func reportErrorAndExit(err error, responseBytes []byte) {
	client.PrintMessage("Failed to unmarshal response. Error: %s", err)
	client.PrintMessage("Original data follows:")
	client.PrintMessageAndExit(string(responseBytes))
}

func describe() {
	// TODO: figure out KingPin's error handling
	requestContent, err := json.Marshal(describeRequest{config.ServiceName})
	if err != nil {
		client.PrintMessageAndExit(err.Error())
	}
	responseBytes, err := client.HTTPCosmosPostJSON("describe", string(requestContent))
	if err != nil {
		client.PrintMessageAndExit(err.Error())
	}
	// This attempts to retrieve resolvedOptions from the response. This field is only provided by
	// Cosmos running on Enterprise DC/OS 1.10 clusters or later.
	resolvedOptionsBytes, err := client.GetValueFromJSONResponse(responseBytes, "resolvedOptions")
	checkError(err, responseBytes)
	if resolvedOptionsBytes != nil {
		client.PrintJSONBytes(resolvedOptionsBytes)
	} else {
		client.PrintMessage("Package configuration is not available for service %s.", config.ServiceName)
		client.PrintMessage("dcos %s %s is only available for packages installed with Enterprise DC/OS 1.10 or newer.", config.ModuleName, config.Command)
	}
}

func (cmd *describeHandler) handleDescribe(a *kingpin.Application, e *kingpin.ParseElement, c *kingpin.ParseContext) error {
	config.Command = c.SelectedCommand.FullCommand()
	describe()
	return nil
}

// HandleDescribe adds the describe subcommand to the passed in kingpin.Application.
func HandleDescribe(app *kingpin.Application) {
	cmd := &describeHandler{}
	app.Command("describe", "View the package configuration for this DC/OS service").Action(cmd.handleDescribe)
}

type updateHandler struct {
	UpdateName     string
	OptionsFile    string
	PackageVersion string
	ViewStatus     bool
}

type updateRequest struct {
	AppID          string                 `json:"appId"`
	PackageVersion string                 `json:"packageVersion,omitempty"`
	OptionsJSON    map[string]interface{} `json:"options,omitempty"`
	Replace        bool                   `json:"replace"`
}

func printPackageVersions() {
	// TODO: figure out KingPin's error handling
	requestContent, _ := json.Marshal(describeRequest{config.ServiceName})
	responseBytes, err := client.HTTPCosmosPostJSON("describe", string(requestContent))
	if err != nil {
		client.PrintMessageAndExit(err.Error())
	}
	packageBytes, err := client.GetValueFromJSONResponse(responseBytes, "package")
	checkError(err, responseBytes)
	currentVersionBytes, err := client.GetValueFromJSONResponse(packageBytes, "version")
	checkError(err, responseBytes)
	downgradeVersionsBytes, err := client.GetValueFromJSONResponse(responseBytes, "downgradesTo")
	checkError(err, responseBytes)
	downgradeVersions, err := client.JSONBytesToArray(downgradeVersionsBytes)
	checkError(err, responseBytes)
	upgradeVersionsBytes, err := client.GetValueFromJSONResponse(responseBytes, "upgradesTo")
	checkError(err, responseBytes)
	updateVersions, err := client.JSONBytesToArray(upgradeVersionsBytes)
	checkError(err, responseBytes)
	client.PrintMessage("Current package version is: %s", currentVersionBytes)
	if len(downgradeVersions) > 0 {
		client.PrintMessage("Package can be downgraded to: %s", client.PrettyPrintSlice(downgradeVersions))
	} else {
		client.PrintMessage("No valid package downgrade versions.")
	}
	if len(updateVersions) > 0 {
		client.PrintMessage("Package can be upgraded to: %s", client.PrettyPrintSlice(updateVersions))
	} else {
		client.PrintMessage("No valid package upgrade versions.")
	}
}

func (cmd *updateHandler) ViewPackageVersions(a *kingpin.Application, e *kingpin.ParseElement, c *kingpin.ParseContext) error {
	config.Command = c.SelectedCommand.FullCommand()
	printPackageVersions()
	return nil
}

func readJSONFile(filename string) (map[string]interface{}, error) {
	fileBytes, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	return client.UnmarshalJSON(fileBytes)
}

func parseUpdateResponse(responseBytes []byte) (string, error) {
	responseJSON, err := client.UnmarshalJSON(responseBytes)
	if err != nil {
		return "", err
	}
	return string(responseJSON["marathonDeploymentId"].(string)), nil
}

func doUpdate(optionsFile, packageVersion string) {
	// TODO: figure out KingPin's error handling
	request := updateRequest{AppID: config.ServiceName, Replace: false}
	if len(packageVersion) == 0 && len(optionsFile) == 0 {
		client.PrintMessage("Either --options and/or --package-version must be specified. See --help.")
		return
{'fraction_non_alphanumeric': 0.11045166482245247, 'fraction_numerical': 0.0030178050497937834, 'mean_word_length': 5.601593625498008, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1642356', 'n_tokens_mistral': 2995, 'n_tokens_neox': 2895, 'n_words': 722}