Skip to content

Commit

Permalink
Merge pull request #396 from gphotosuploader/issue-393
Browse files Browse the repository at this point in the history
Add Album configuration option to set the Album target in Google Photos
  • Loading branch information
pacoorozco committed Oct 23, 2023
2 parents fd7501c + 01a8cea commit e97bfa0
Show file tree
Hide file tree
Showing 24 changed files with 293 additions and 61 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/) and this
## 4.1.0
### Added
- Flag `--port` to configure the port where the authentication server will listen to when using the `auth` command ([#370][i370])
- **New command to reset the already uploaded file tracker** (`reset file-tracker`), which removes the internal database ([#182][i182])
- New **`Album` option in Job's configuration** which allows to set a fixed album's name to upload objects to. ([#393][i393])

### Deprecated
- The `CreateAlbums` option in Job's configuration is deprecated in favor of a new `Album` option.

[i370]: https://github.com/gphotosuploader/gphotos-uploader-cli/issues/370
[i182]: https://github.com/gphotosuploader/gphotos-uploader-cli/issues/182
[i393]: https://github.com/gphotosuploader/gphotos-uploader-cli/issues/393

## 4.0.0
### Added
Expand Down
14 changes: 13 additions & 1 deletion internal/cli/push/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,18 @@ func (cmd *PushCmd) Run(cobraCmd *cobra.Command, args []string) error {

// launch all folder upload jobs
for _, config := range cli.Config.Jobs {

//nolint:staticcheck // CreateAlbums is maintained for backwards compatibility
if config.CreateAlbums != "" {
cli.Logger.Warn("Deprecated 'CreateAlbums' option is used in configuration. Please, use the 'Album' option instead.")
}

// TODO: Translate the deprecated CreateAlbums into the Albums option for backwards compatibility
//nolint:staticcheck // CreateAlbums is maintained for backwards compatibility
if config.Album == "" && config.CreateAlbums != "" && config.CreateAlbums != "Off" {
config.Album = "auto:" + config.CreateAlbums
}

sourceFolder := config.SourceFolder

filterFiles, err := filter.Compile(config.IncludePatterns, config.ExcludePatterns)
Expand All @@ -71,7 +83,7 @@ func (cmd *PushCmd) Run(cobraCmd *cobra.Command, args []string) error {
FileTracker: cli.FileTracker,

SourceFolder: sourceFolder,
CreateAlbums: config.CreateAlbums,
Album: config.Album,
Filter: filterFiles,
}

Expand Down
38 changes: 34 additions & 4 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"fmt"
"io"
"path/filepath"
"strings"

"github.com/hjson/hjson-go/v4"
"github.com/mitchellh/go-homedir"
Expand Down Expand Up @@ -106,7 +107,7 @@ func readFile(fs afero.Fs, filename string) (*Config, error) {
return nil, err
}

// convert all path to absolute paths.
// convert all paths to absolute paths.
if err := config.ensureSourceFolderAbsolutePaths(); err != nil {
return nil, err
}
Expand Down Expand Up @@ -141,7 +142,11 @@ func (c Config) validateJobs(fs afero.Fs) error {
if !exist {
return fmt.Errorf("folder '%s' does not exist", job.SourceFolder)
}
if !isValidCreateAlbums(job.CreateAlbums) {
if job.Album != "" && !isValidAlbum(job.Album) {
return fmt.Errorf("option Album is invalid, '%s", job.Album)
}
// TODO: Check CreateAlbums for backwards compatibility. It should be removed on version 5.x
if job.Album == "" && !isValidCreateAlbums(job.CreateAlbums) {
return fmt.Errorf("option CreateAlbums is invalid, '%s", job.CreateAlbums)
}
}
Expand All @@ -159,7 +164,7 @@ func (c Config) validateSecretsBackendType() error {

func (c Config) ensureSourceFolderAbsolutePaths() error {
for i := range c.Jobs {
item := &c.Jobs[i] // we do that way to modify original object while iterating.
item := &c.Jobs[i] // we do that way to modify an original object while iterating.
src, err := homedir.Expand(item.SourceFolder)
if err != nil {
return err
Expand All @@ -169,6 +174,31 @@ func (c Config) ensureSourceFolderAbsolutePaths() error {
return nil
}

func isValidAlbumGenerationMethod(method string) bool {
if method != "folderPath" && method != "folderName" {
return false
}
return true
}

// isValidAlbum checks if the value is a valid Album option.
func isValidAlbum(value string) bool {
before, after, found := strings.Cut(value, ":")
if !found {
return false
}
if after == "" {
return false
}
switch before {
case "name":
return true
case "auto":
return isValidAlbumGenerationMethod(after)
}
return false
}

// isValidCreateAlbums checks if the value is a valid CreateAlbums option.
func isValidCreateAlbums(value string) bool {
switch value {
Expand Down Expand Up @@ -218,7 +248,7 @@ func defaultSettings() Config {
Jobs: []FolderUploadJob{
{
SourceFolder: "YOUR_FOLDER_PATH",
CreateAlbums: "folderName",
Album: "",
DeleteAfterUpload: false,
},
},
Expand Down
25 changes: 17 additions & 8 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,22 @@ func TestFromFile(t *testing.T) {
want string
isErrExpected bool
}{
{"Should success", "testdata/valid-config/config.hjson", "[email protected]", false},
{"Should fail if dir does not exist", "testdata/non-existent/config.hjson", "", true},
{"Should fail if Account is invalid", "testdata/invalid-config/Account.hjson", "", true},
{"Should fail if SourceFolder does not exist", "testdata/invalid-config/SourceFolder.hjson", "", true},
{"Should fail if SecretsBackendType is invalid", "testdata/invalid-config/SecretsBackendType.hjson", "", true},
{"Should fail if AppAPICredentials are invalid", "testdata/invalid-config/AppAPICredentials.hjson", "", true},
{"Should fail if CreateAlbums is invalid", "testdata/invalid-config/CreateAlbums.hjson", "", true},
{"Should success with Album's name option", "testdata/valid-config/configWithAlbumNameOption.hjson", "[email protected]", false},
{"Should success with Album's auto folderName option", "testdata/valid-config/configWithAlbumAutoFolderNameOption.hjson", "[email protected]", false},
{"Should success with Album's auto folderPath option", "testdata/valid-config/configWithAlbumAutoFolderPathOption.hjson", "[email protected]", false},
{"Should success with deprecated CreateAlbums option", "testdata/valid-config/configWithDeprecatedCreateAlbumsOption.hjson", "[email protected]", false},

{"Should fail if config dir does not exist", "testdata/non-existent/config.hjson", "", true},
{"Should fail if Account is invalid", "testdata/invalid-config/EmptyAccount.hjson", "", true},
{"Should fail if SourceFolder does not exist", "testdata/invalid-config/NonExistentSourceFolder.hjson", "", true},
{"Should fail if SecretsBackendType is invalid", "testdata/invalid-config/BadSecretsBackendType.hjson", "", true},
{"Should fail if AppAPICredentials are invalid", "testdata/invalid-config/EmptyAppAPICredentials.hjson", "", true},
{"Should fail if Jobs is empty", "testdata/invalid-config/NoJobs.hjson", "", true},
{"Should fail if Album's format is invalid", "testdata/invalid-config/AlbumBadFormat.hjson", "", true},
{"Should fail if Album's key is invalid", "testdata/invalid-config/AlbumBadKey.hjson", "", true},
{"Should fail if Album's name is invalid", "testdata/invalid-config/AlbumEmptyName.hjson", "", true},
{"Should fail if Album's auto value is invalid", "testdata/invalid-config/AlbumBadAutoValue.hjson", "", true},
{"Should fail if deprecated CreateAlbums is invalid", "testdata/invalid-config/DeprecatedCreateAlbums.hjson", "", true},
}

for _, tc := range testCases {
Expand Down Expand Up @@ -100,14 +108,15 @@ func TestConfig_SafePrint(t *testing.T) {
Jobs: []config.FolderUploadJob{
{
SourceFolder: "foo",
Album: "name:albumName",
CreateAlbums: "folderPath",
DeleteAfterUpload: false,
IncludePatterns: []string{},
ExcludePatterns: []string{},
},
},
}
want := `{"APIAppCredentials":{"ClientID":"client-id","ClientSecret":"REMOVED"},"Account":"account","SecretsBackendType":"auto","Jobs":[{"SourceFolder":"foo","CreateAlbums":"folderPath","DeleteAfterUpload":false,"IncludePatterns":[],"ExcludePatterns":[]}]}`
want := `{"APIAppCredentials":{"ClientID":"client-id","ClientSecret":"REMOVED"},"Account":"account","SecretsBackendType":"auto","Jobs":[{"SourceFolder":"foo","Album":"name:albumName","CreateAlbums":"folderPath","DeleteAfterUpload":false,"IncludePatterns":[],"ExcludePatterns":[]}]}`

if want != cfg.SafePrint() {
t.Errorf("want: %s, got: %s", want, cfg.SafePrint())
Expand Down
18 changes: 16 additions & 2 deletions internal/config/schema.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package config

// Config represents the content of configuration file.
// Config represents the content of the configuration file.
// It defines the schema for Marshal and Unmarshal the data of the configuration file.
type Config struct {
// APIAppCredentials represents Google Photos API credentials for OAuth.
Expand Down Expand Up @@ -29,8 +29,22 @@ type FolderUploadJob struct {
// SourceFolder is the folder containing the objects to be uploaded.
SourceFolder string `json:"SourceFolder"`

// Album is the album where objects will be uploaded.
// If the Album option is not set, the objects will not be associated with an album in Google Photos.
//
// These are the valid values: "name:", "id:" and "auto:".
// "name:" : Followed by the album name in Google Photos (album names are not unique, so the first to match
// will be selected)
// "auto:" : Followed either "folderPath" or "folderName" will use an autogenerated album name based on the
// object's folder path or object's folder name.
Album string `json:"Album,omitempty"`

// CreateAlbums is the parameter to create albums on Google Photos.
// Valid options are:
//
// Deprecated: CreateAlbums exists to maintain backwards compatibility with version 4.x. It should not be used in
// favor of the Album option.
//
// Valid options were:
// Off: Disable album creation (default).
// folderPath: Creates album with the name based on full folder path.
// folderName: Creates album with the name based on the folder name.
Expand Down
19 changes: 19 additions & 0 deletions internal/config/testdata/invalid-config/AlbumAuto.hjson
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
APIAppCredentials:
{
ClientID: client-id
ClientSecret: client-secret
}
Account: [email protected]
SecretsBackendType: auto
Jobs:
[
{
SourceFolder: ./testdata
Album: auto:invalid
DeleteAfterUpload: false
IncludePatterns: []
ExcludePatterns: []
}
]
}
19 changes: 19 additions & 0 deletions internal/config/testdata/invalid-config/AlbumBadFormat.hjson
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
APIAppCredentials:
{
ClientID: client-id
ClientSecret: client-secret
}
Account: [email protected]
SecretsBackendType: auto
Jobs:
[
{
SourceFolder: ./testdata
Album: invalid
DeleteAfterUpload: false
IncludePatterns: []
ExcludePatterns: []
}
]
}
19 changes: 19 additions & 0 deletions internal/config/testdata/invalid-config/AlbumBadKey.hjson
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
APIAppCredentials:
{
ClientID: client-id
ClientSecret: client-secret
}
Account: [email protected]
SecretsBackendType: auto
Jobs:
[
{
SourceFolder: ./testdata
Album: invalid:fooBar
DeleteAfterUpload: false
IncludePatterns: []
ExcludePatterns: []
}
]
}
19 changes: 19 additions & 0 deletions internal/config/testdata/invalid-config/AlbumEmptyName.hjson
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
APIAppCredentials:
{
ClientID: client-id
ClientSecret: client-secret
}
Account: [email protected]
SecretsBackendType: auto
Jobs:
[
{
SourceFolder: ./testdata
Album: name:
DeleteAfterUpload: false
IncludePatterns: []
ExcludePatterns: []
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,9 @@
[
{
SourceFolder: ./testdata
CreateAlbums: folderName
DeleteAfterUpload: false
IncludePatterns: []
ExcludePatterns: []
}
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,9 @@
[
{
SourceFolder: ./testdata
CreateAlbums: folderName
DeleteAfterUpload: false
IncludePatterns: []
ExcludePatterns: []
}
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,9 @@
[
{
SourceFolder: ./testdata
CreateAlbums: folderName
DeleteAfterUpload: false
IncludePatterns: []
ExcludePatterns: []
}
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,9 @@
[
{
SourceFolder: ./testdata/non-existent
CreateAlbums: folderName
DeleteAfterUpload: false
IncludePatterns: []
ExcludePatterns: []
}
]
}
}
3 changes: 1 addition & 2 deletions internal/config/testdata/valid-config/config.hjson
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,9 @@
[
{
SourceFolder: ./testdata/valid-config
CreateAlbums: folderName
DeleteAfterUpload: false
IncludePatterns: []
ExcludePatterns: []
}
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
APIAppCredentials:
{
ClientID: client-id
ClientSecret: client-secret
}
Account: [email protected]
SecretsBackendType: auto
Jobs:
[
{
SourceFolder: ./testdata/valid-config
Album: auto:folderName
DeleteAfterUpload: false
IncludePatterns: []
ExcludePatterns: []
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
APIAppCredentials:
{
ClientID: client-id
ClientSecret: client-secret
}
Account: [email protected]
SecretsBackendType: auto
Jobs:
[
{
SourceFolder: ./testdata/valid-config
Album: name:albumName
DeleteAfterUpload: false
IncludePatterns: []
ExcludePatterns: []
}
]
}
Loading

0 comments on commit e97bfa0

Please sign in to comment.