diff --git a/README.md b/README.md
index 5e3aff8..ff78968 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@ ggsrun
# Overview
-This is a CLI tool to execute Google Apps Script (GAS) on a terminal.
+This is a CLI tool to execute Google Apps Script (GAS) on a terminal. Also this CLI tool can be used for managing files in Google Drive for OAuth2 and Service Account.
# Demo
@@ -15,7 +15,7 @@ This is a CLI tool to execute Google Apps Script (GAS) on a terminal.
# Description
-Will you want to develop GAS on your local PC? Generally, when we develop GAS, we have to login to Google using own browser and develop it on the Script Editor. Recently, I have wanted to have more convenient local-environment for developing GAS. So I created this "ggsrun". The main work is to execute GAS on local terminal and retrieve the results from Google.
+Will you want to develop GAS on your local PC? Generally, when we develop GAS, we have to login to Google using own browser and develop it on the Script Editor. Recently, I have wanted to have more convenient local-environment for developing GAS. So I created this "ggsrun". The main work is to execute GAS on local terminal and retrieve the results from Google. Furthermore, this tool can be also used for managing files in own Google Drive (OAuth2) and Google Drive for Service Account.
Features of "ggsrun" are as follows.
@@ -32,6 +32,9 @@ Features of "ggsrun" are as follows.
1. **[Rearranges scripts in project.](help/README.md#rearrangescripts)** Updated! (v1.4.0)
1. **[Modifies Manifests in project.](help/README.md#modifymanifests)**
1. **[Seach files in Google Drive using search query and regex](help/README.md#searchfilesusingregex)** Updated! (v1.6.0)
+1. **[Manage Permissions of files](help/README.md#managepermissions)** Updated! (v1.7.0)
+1. **[Get Drive Information.](help/README.md#getdriveinformation)** Updated! (v1.7.0)
+1. **[ggsrun got to be able to be used by not only OAuth2, but also Service Account.](help/README.md#useserviceaccount)** Updated! (v1.7.0)
# How to Install
@@ -94,6 +97,11 @@ Please reauthorize to include a new scope to the access token as follows.
Completed!
+
+# From version 1.7.0, ggsrun can access to Google Drive using Service Account. Updated! (v1.7.0)
+ggsrun can access to Google Drive using [Service Account](https://developers.google.com/identity/protocols/OAuth2ServiceAccount). When OAuth2 is used, you can see the files and folders in own Google Drive. When Service Account is used, you can see them in Google Drive for Service Account. Namely, the Drive for OAuth2 is different from that for Service Account. Please be careful this. And when Service Account is used, there are what it can do and what it can't do. About them, please read [here](help/README.md#useserviceaccount).
+
+
# How to use ggsrun
1. [Executes GAS and Retrieves Result Values](help/README.md#executesgasandretrievesresultvalues)
1. [Executes GAS with Values and Retrieves Feedbacked Values](help/README.md#executesgaswithvaluesandretrievesfeedbackedvalues)
@@ -110,6 +118,9 @@ Completed!
1. [Rearrange Script in Project](help/README.md#rearrangescripts)
1. [Modify Manifests](help/README.md#modifymanifests)
1. [Seach Files using Query and Regex](help/README.md#searchfilesusingregex)
+1. [Manage Permissions of files](#managepermissions)
+1. [Get Drive Information.](#getdriveinformation)
+1. [ggsrun got to be able to be used by not only OAuth2, but also Service Account.](#useserviceaccount)
# Applications
1. [For Sublime Text](help/README.md#demosublime)
diff --git a/doc.go b/doc.go
index c3d8f65..9fe8483 100644
--- a/doc.go
+++ b/doc.go
@@ -30,7 +30,13 @@ Will you want to develop GAS on your local PC? Generally, when we develop GAS, w
12. Modifies Manifests in project.
-13. Seach files in Google Drive using search query and regex
+13. Seach files in Google Drive using search query and regex.
+
+14. Manage Permissions of files.
+
+15. Get Drive Information.
+
+16. ggsrun got to be able to be used by not only OAuth2, but also Service Account from v1.7.0.
You can see the release page https://github.com/tanaikech/ggsrun/releases
diff --git a/ggsrun.go b/ggsrun.go
index 17287a5..e19cd8b 100644
--- a/ggsrun.go
+++ b/ggsrun.go
@@ -14,8 +14,8 @@ func main() {
app.Name = appname
app.Author = "Tanaike [ https://github.com/tanaikech/ggsrun ] "
app.Email = "tanaike@hotmail.com"
- app.Usage = "This is an application of Google Drive and Google Apps Script (GAS)."
- app.Version = "1.6.0"
+ app.Usage = "This is a CLI application for managing Google Drive and Google Apps Script (GAS)."
+ app.Version = "1.7.0"
app.Commands = []cli.Command{
{
Name: "exe1",
@@ -159,6 +159,10 @@ func main() {
Name: "extension, e",
Usage: "Extension (File format of downloaded file)",
},
+ cli.StringFlag{
+ Name: "mimetype, m",
+ Usage: "mimeType (You can retrieve only files with the specific mimeType, when files are downloaded from a folder.) ex. '-m \"mimeType1,mimeType2\"'",
+ },
cli.BoolFlag{
Name: "rawdata, r",
Usage: "Save a project with GAS scripts as raw data (JSON data).",
@@ -187,6 +191,10 @@ func main() {
Name: "jsonparser, j",
Usage: "Display results by JSON parser",
},
+ cli.StringFlag{
+ Name: "serviceaccount, sa",
+ Usage: "Value is filename and path of credentials.json which was retrieved by creating Service Account.",
+ },
},
},
{
@@ -242,6 +250,10 @@ func main() {
Name: "jsonparser, j",
Usage: "Display results by JSON parser",
},
+ cli.StringFlag{
+ Name: "serviceaccount, sa",
+ Usage: "Value is filename and path of credentials.json which was retrieved by creating Service Account.",
+ },
},
},
{
@@ -312,6 +324,10 @@ func main() {
Name: "jsonparser, j",
Usage: "Display results by JSON parser",
},
+ cli.StringFlag{
+ Name: "serviceaccount, sa",
+ Usage: "Value is filename and path of credentials.json which was retrieved by creating Service Account.",
+ },
},
},
{
@@ -341,6 +357,10 @@ func main() {
Name: "jsonparser, j",
Usage: "Display results by JSON parser",
},
+ cli.StringFlag{
+ Name: "serviceaccount, sa",
+ Usage: "Value is filename and path of credentials.json which was retrieved by creating Service Account.",
+ },
},
},
{
@@ -366,6 +386,81 @@ func main() {
Name: "jsonparser, j",
Usage: "Display results by JSON parser",
},
+ cli.StringFlag{
+ Name: "serviceaccount, sa",
+ Usage: "Value is filename and path of credentials.json which was retrieved by creating Service Account.",
+ },
+ },
+ },
+ {
+ Name: "permissions",
+ Aliases: []string{"p"},
+ Usage: "Manage file permissions.",
+ Description: "In this mode, an access token is required.",
+ Action: managePermissions,
+ Flags: []cli.Flag{
+ cli.StringFlag{
+ Name: "fileid, fi",
+ Usage: "Value is file ID. This value is required.",
+ },
+ cli.StringFlag{
+ Name: "permissionid, pi",
+ Usage: "Value is permission ID. This ID can be retrieved by retrieving permission list.",
+ },
+ cli.BoolFlag{
+ Name: "create, c",
+ Usage: "Create new permissions.",
+ },
+ cli.BoolFlag{
+ Name: "delete, d",
+ Usage: "Delete permissions. fileId and permissionId are required.",
+ },
+ cli.StringFlag{
+ Name: "role",
+ Usage: "The role granted by this permission. While new values may be supported in the future, the following are currently allowed: owner, organizer, fileOrganizer, writer, commenter, reader",
+ },
+ cli.StringFlag{
+ Name: "type",
+ Usage: "The type of the grantee. Valid values are: user, group, domain, anyone",
+ },
+ cli.StringFlag{
+ Name: "emailaddress, email",
+ Usage: "The email address of the user or group to which this permission refers.",
+ },
+ cli.BoolFlag{
+ Name: "transferownership, transfer",
+ Usage: "Whether to transfer ownership to the specified user and downgrade the current owner to a writer. This parameter is required as an acknowledgement of the side effect. (Default: false)",
+ },
+ cli.BoolFlag{
+ Name: "jsonparser, j",
+ Usage: "Display results by JSON parser",
+ },
+ cli.StringFlag{
+ Name: "serviceaccount, sa",
+ Usage: "Value is filename and path of credentials.json which was retrieved by creating Service Account.",
+ },
+ },
+ },
+ {
+ Name: "driveinformation",
+ Aliases: []string{"di"},
+ Usage: "Get drive information.",
+ Description: "In this mode, an access token is required.",
+ Action: getDriveInformation,
+ Flags: []cli.Flag{
+ cli.StringFlag{
+ Name: "fields, f",
+ Usage: "Fields for retrieving files.",
+ Value: "storageQuota,user",
+ },
+ cli.BoolFlag{
+ Name: "jsonparser, j",
+ Usage: "Display results by JSON parser",
+ },
+ cli.StringFlag{
+ Name: "serviceaccount, sa",
+ Usage: "Value is filename and path of credentials.json which was retrieved by creating Service Account.",
+ },
},
},
{
diff --git a/handler.go b/handler.go
index 4b2de75..cb1018f 100644
--- a/handler.go
+++ b/handler.go
@@ -113,6 +113,28 @@ func searchFilesByQueryAndRegex(c *cli.Context) error {
return nil
}
+// managePermissions : Manage permissions.
+func managePermissions(c *cli.Context) error {
+ res := defAuthContainer(c).
+ ggsrunIni(c).
+ goauth().
+ defPermissionsContainer(c).
+ ManagePermissions()
+ dispTransferResult(c, res)
+ return nil
+}
+
+// getDriveInformation : Get drive information.
+func getDriveInformation(c *cli.Context) error {
+ res := defAuthContainer(c).
+ ggsrunIni(c).
+ goauth().
+ defDownloadContainer(c).
+ GetDriveInformation()
+ dispTransferResult(c, res)
+ return nil
+}
+
// reAuth : Retrieve tokens again.
func reAuth(c *cli.Context) error {
defAuthContainer(c).
diff --git a/help/README.md b/help/README.md
index bd5bbd2..7eb8149 100644
--- a/help/README.md
+++ b/help/README.md
@@ -34,6 +34,10 @@ ggsrun
- Retrieve Revision Files and Versions of Projects
- Rearrange Script in Project
- Modify Manifests
+ - Seach Files using Query and Regex
+ - Manage Permissions
+ - Get Drive Information
+ - Use Service Account
- [Applications](#applications)
- For Sublime Text
- For CoffeeScript
@@ -85,6 +89,12 @@ Features of "ggsrun" are as follows.
1. **[Seach files in Google Drive using search query and regex](#searchfilesusingregex)**
+1. **[Manage Permissions of files](#managepermissions)**
+
+1. **[Get Drive Information.](#getdriveinformation)**
+
+1. **[ggsrun got to be able to be used by not only OAuth2, but also Service Account.](#useserviceaccount)**
+
You can see the release page [here](https://github.com/tanaikech/ggsrun/releases).
@@ -920,7 +930,7 @@ $ ggsrun d --deletefile [fileId]
### 6-1. Download All Files and Folders in Specific Folder
-From version 1.5.0, ggsrun got to be able to download all files and folders in the specific folder in Google Drive. The same folder structure of Google Drive is created to the local PC.
+From version 1.5.0, ggsrun got to be able to download all files and folders in the specific folder in Google Drive. The same folder structure of Google Drive is created to the local PC. In this function, when the folder is shared by other account, ggsrun can also download all files with the folder structure from the shared folder.
**Run :**
@@ -1191,8 +1201,38 @@ Result includes file name, file id, modified time and URL.
> **Search of scripts :** You can search standalone scripts using filename and fileId. But the container-bound scripts cannot be searched by filename, while it can be searched by fileId. Because parentId cannot be retrieved using Apps Script API yet. About this, I reported about this to [Google Issue Tracker](https://issuetracker.google.com/issues/71941200).
+
+## 10. Seach Files using Query and Regex
+Although at ggsrun, files can be searched by filename and file ID, searching files using [search query](https://developers.google.com/drive/api/v3/search-parameters) and regex couldn't be done. From version 1.6.0, files got to be able to be searched using the search query and regex.
+
+The flow of this function is as follows.
+
+1. Retrieve all files in Google Drive using the search query and fields.
+1. Retrieve files from the retrieved files using regex.
+ - Regex is used to the filename.
+
+The command is as follows.
+
+~~~bash
+$ ggsrun sf -q "### search query ###" -f "### fields ###" -r "### regex ###"
+~~~
+
+- Search query: [Document](https://developers.google.com/drive/api/v3/search-parameters)
+- Fields: [Document](https://developers.google.com/drive/api/v3/reference/files#resource)
+- Regex: [Document](https://github.com/google/re2/wiki/Syntax)
+
+#### Sample command
+~~~bash
+ggsrun sf \
+ -q "mimeType='application/vnd.google-apps.spreadsheet'" \
+ -f "files(id,name)" \
+ -r "sample\d{1}"
+~~~
+
+This command retrieves files with Google Spreadsheet and the filename of ``sample#`` (``#`` is 0-9.). The file ID and filename of files are returned.
+
-## 10. Update Project
+## 11. Update Project
It updates existing project on Google Drive. You can update standalone script and container-bound script.
**Run :**
@@ -1221,7 +1261,7 @@ In this demonstration, create new Spreadsheet and upload 5 files as new project
-## 11. Retrieve Revision Files and Versions of Projects
+## 12. Retrieve Revision Files and Versions of Projects
It retrieves revisions for files on Google Drive and retrieves versions for projects.
**Display revision and version ID list for file ID :**
@@ -1272,7 +1312,7 @@ $ ggsrun r -i [File ID] -cv [description of version]
-## 12. Rearrange Script in Project
+## 13. Rearrange Script in Project
Have you ever thought about rearranging Google Apps Scripts in a project which can be seen at the script editor? I also have thought about it. Finally, I could find the workaround to do it. From ggsrun with v1.3.2, scripts in a project can be rearranged. And also, from v1.4.0, it gets to be able to rearrange both standalone script and container-bound script.
If you want to rearrange using a GUI application, please check [RearrangeScripts](https://github.com/tanaikech/RearrangeScripts).
@@ -1336,7 +1376,7 @@ In this demonstration, scripts in a project is intaractively rearranged. After r
If you want to use add-on created by Google Apps Script. Please check [here](https://github.com/tanaikech/RearrangeScripts).
-## 13. Modify Manifests
+## 14. Modify Manifests
[At October 24, 2017, "Manifests" which is new function for controlling the properties of Google Apps Script was added (GAS).](https://developers.google.com/apps-script/) You can see the detail of "Manifests" [here](https://developers.google.com/apps-script/concepts/manifests). The manifests can be seen as the following flow.
- On the script editor.
@@ -1406,35 +1446,150 @@ Awesome points of Manifests that I think are below.
In this demonstration, Advanced Google Services are modified by modifying the manifests.
-
-## 14. Seach Files using Query and Regex
-Although at ggsrun, files can be searched by filename and file ID, searching files using [search query](https://developers.google.com/drive/api/v3/search-parameters) and regex couldn't be done. From version 1.6.0, files got to be able to be searched using the search query and regex.
+
+## 15. Manage Permissions
+In the current stage, ggsrun can get, create and delete permissions.
-The flow of this function is as follows.
+##### Get permission list
+~~~bash
+$ ggsrun p -fi ### fileId ###
+~~~
-1. Retrieve all files in Google Drive using the search query and fields.
-1. Retrieve files from the retrieved files using regex.
- - Regex is used to the filename.
+##### Get permissions
+~~~bash
+$ ggsrun p -fi "### fileId ###" -pi "### permissionId ###"
+~~~
-The command is as follows.
+##### Create permissions
+~~~bash
+$ ggsrun p -c -fi "### fileId ###" -role writer -type user -email "### email ###"
+~~~
+##### Delete permissions
~~~bash
-$ ggsrun sf -q "### search query ###" -f "### fields ###" -r "### regex ###"
+$ ggsrun p -d -fi "### fileId ###" -pi "### permissionId ###"
~~~
-- Search query: [Document](https://developers.google.com/drive/api/v3/search-parameters)
-- Fields: [Document](https://developers.google.com/drive/api/v3/reference/files#resource)
-- Regex: [Document](https://github.com/google/re2/wiki/Syntax)
-#### Sample command
+
+## 16. Get Drive Information
+When a Service Account is created, new storage of Google Drive (15 GB for free account) is given. This storage quota is different from that of own account. So it is required to know the drive information.
+
+**For OAuth 2** : In this command, you can retrieve the drive information of own account.
+
~~~bash
-ggsrun sf \
- -q "mimeType='application/vnd.google-apps.spreadsheet'" \
- -f "files(id,name)" \
- -r "sample\d{1}"
+$ ggsrun di
~~~
-This command retrieves files with Google Spreadsheet and the filename of ``sample#`` (``#`` is 0-9.). The file ID and filename of files are returned.
+**For Service Account** : In this command, you can retrieve the drive information of Service account.
+
+~~~bash
+$ ggsrun di -sa credentials.json
+~~~
+
+#### Returned value
+Default fields are ``storageQuota,user``. Of course, you can modify fields using the option of ``^f``.
+
+~~~
+{
+ "storageQuota": {
+ "limit": "#####",
+ "usage": "#####",
+ "usageInDrive": "#####",
+ "usageInDriveTrash": "#####"
+ },
+ "user": {
+ "displayName": "#####",
+ "emailAddress": "#####",
+ "kind": "drive#user",
+ "me": true,
+ "permissionId": "#####",
+ "photoLink": "#####" // When service account is used, this property is not included.
+ }
+}
+~~~
+
+
+
+## 17. Use Service Account
+From version 1.7.0, ggsrun got to be able to access to Google Drive using Service Account. About Service Account, please check [here](https://developers.google.com/identity/protocols/OAuth2ServiceAccount).
+
+- When OAuth2 is used, you can see the files and folders in own Google Drive.
+- When Service Account is used, you can see them in Google Drive for Service Account.
+
+Namely, the Drive for OAuth2 is different from that for Service Account. Please be careful this. And there are several limitations for using Service Account.
+
+1. [Service Account cannot use Google Apps Script API.](https://developers.google.com/apps-script/api/concepts/)
+1. When Service Account is used, scripts cannot be also uploaded, downloaded and updated using Drive API.
+
+By above, scripts cannot be uploaded, downloaded, updated and executed using Service Account.
+
+On the other hand, what Service Account can do with ggsrun is as follows.
+
+### Options for Service Account
+Basically, when you use Service Account, please add an option of ``-sa credentials.json``. ``-sa`` and ``credentials.json`` are the option for using Service Account and JSON file including credentials retrieved by creating Service Account, respectively.
+
+#### 1. Get file list of Google Drive
+
+~~~bash
+$ ggsrun ls -sa credentials.json -s
+~~~
+
+or
+
+~~~bash
+$ ggsrun d -sa credentials.json -i root -l
+~~~
+
+By this, the metadata of all files in Google Drive of Service Account can be retrieved.
+
+
+#### 2. Download files
+~~~bash
+$ ggsrun d -sa credentials.json -f sample.txt
+~~~
+
+#### 3. Upload files
+~~~bash
+$ ggsrun u -sa credentials.json -f sample.txt
+~~~
+
+#### 4. Search files
+~~~bash
+$ ggsrun sf -sa credentials.json -q "name contains 'sample'"
+~~~
+
+#### 5. Manage permissions of files
+When Service Account is used, the management of permissions of files is important. So I added this. In the current stage, ggsrun can get, create and delete permissions.
+
+##### Get permission list
+~~~bash
+$ ggsrun p -sa credentials.json -fi ### fileId ###
+~~~
+
+##### Get permissions
+~~~bash
+$ ggsrun p -sa credentials.json -fi "### fileId ###" -pi "### permissionId ###"
+~~~
+
+##### Create permissions
+~~~bash
+$ ggsrun p -sa credentials.json -c -fi "### fileId ###" -role writer -type user -email "### email ###"
+~~~
+
+##### Delete permissions
+~~~bash
+$ ggsrun p -sa credentials.json -d -fi "### fileId ###" -pi "### permissionId ###"
+~~~
+
+#### 6. Get Drive information
+When a Service Account is created, new storage of Google Drive (15 GB for free account) is given. This storage quota is different from that of own account. So it is required to know the drive information.
+
+~~~bash
+$ ggsrun di -sa credentials.json
+~~~
+
+If the command of ``$ ggsrun di`` is run, the drive information with OAuth2 (own account) can be retrieved.
---
diff --git a/help/UpdateHistory.md b/help/UpdateHistory.md
index bfaf9dc..da1e7d2 100644
--- a/help/UpdateHistory.md
+++ b/help/UpdateHistory.md
@@ -111,6 +111,7 @@ ggsrun
- This means that a file with filename is uploaded by each chunk of 10 MB.
+
* v1.5.0 (October 27, 2018)
1. [From this version, ggsrun got to be able to download all files and folders in the specific folder in Google Drive.](README.md#downloadfilesfromfolder) When all files are downloaded from a folder, the same folder structure of Google Drive is created to the local PC.
- ``$ ggsrun d -f folderName or folderId``
@@ -123,11 +124,13 @@ ggsrun
1. Some modifications.
+
* v1.5.1 (November 2, 2018)
1. Removed a bug.
- When a file information was retrieved, createdTime and modifiedTime couldn't be seen and the information was incomplete.
+
* v1.5.2 (November 4, 2018)
1. About [downloading folders](https://github.com/tanaikech/ggsrun/blob/master/help/README.md#downloadfilesfromfolder), when files are downloaded from a folder, you can download Google Docs files with the mimeType you want. For example, when you download files from the folder, if ``-e txt`` is used, Google Docs are downloaded as the text file. When ``-e pdf`` is used, they are downloaded as the PDF file. Of course, there are mimeType which cannot be converted.
- ``$ ggsrun d -f [folderName] -e txt -j``
@@ -135,13 +138,23 @@ ggsrun
- ``$ ggsrun u -f [fileName] -c doc -j``
+
* v1.6.0 (November 30, 2018)
1. Although at ggsrun, files can be searched by filename and file ID, searching files using search query and regex couldn't be done. From version 1.6.0, files got to be able to be searched using the search query and regex.
- ``$ ggsrun sf -q "### search query ###" -f "### fields ###" -r "### regex ###"``
1. Some modifications.
+
+
+* v1.7.0 (December 27, 2018)
+ 1. [Manage permissions of files.](https://github.com/tanaikech/ggsrun/blob/master/help/README.md#managepermissions)
+ 1. [Get Drive Information.](https://github.com/tanaikech/ggsrun/blob/master/help/README.md#getdriveinformation) By this, you can know the storage quotas.
+ 1. [**ggsrun got to be able to be used by not only OAuth2, but also Service Account. By this, using ggsrun, Google Drive for Service Account got to be able to be managed.**](https://github.com/tanaikech/ggsrun/blob/master/help/README.md#useserviceaccount)
+ 1. Some modifications.
+
**You can read "How to install" at [here](https://github.com/tanaikech/ggsrun/blob/master/README.md#howtoinstall).**
+
## Server
* v1.0.0 (April 24, 2017)
diff --git a/materials.go b/materials.go
index 13d29b3..7448309 100644
--- a/materials.go
+++ b/materials.go
@@ -44,13 +44,14 @@ const (
// InitVal : Initial values
type InitVal struct {
- pstart time.Time
- workdir string
- cfgdir string
- usedDir string // "work" for working directory or "env" for directory declared by the environment variable.
- update bool
- log bool
- Port int
+ pstart time.Time
+ workdir string
+ cfgdir string
+ usedDir string // "work" for working directory or "env" for directory declared by the environment variable.
+ update bool
+ log bool
+ Port int
+ useServiceAccount string
}
// ResMsg : Response message also included errors
@@ -301,6 +302,9 @@ func defAuthContainer(c *cli.Context) *AuthContainer {
"https://www.googleapis.com/auth/spreadsheets",
"https://www.googleapis.com/auth/script.projects",
}
+
+ // Use Service Account
+ a.useServiceAccount = c.String("serviceaccount")
return a
}
@@ -351,11 +355,12 @@ func defExecutionContainerWebApps() *ExecutionContainer {
// DefDownloadContainer : Struct container for downloading files
func (a *AuthContainer) defDownloadContainer(c *cli.Context) *utl.FileInf {
p := &utl.FileInf{
- Msgar: a.Msg,
- Accesstoken: a.GgsrunCfg.Accesstoken,
- Workdir: a.InitVal.workdir,
- PstartTime: a.InitVal.pstart,
- FileID: c.String("fileid"),
+ Msgar: a.Msg,
+ Accesstoken: a.GgsrunCfg.Accesstoken,
+ Workdir: a.InitVal.workdir,
+ PstartTime: a.InitVal.pstart,
+ UseServiceAccount: a.InitVal.useServiceAccount,
+ FileID: c.String("fileid"),
ProjectID: func(c *cli.Context) string {
id := c.String("projectid")
if c.String("fileid") != "" && c.String("projectid") != "" {
@@ -375,6 +380,12 @@ func (a *AuthContainer) defDownloadContainer(c *cli.Context) *utl.FileInf {
SearchQuery: c.String("query"),
SearchFields: c.String("fields"),
SearchRegex: c.String("regex"),
+ InputtedMimeType: func(mime string) []string {
+ if mime != "" {
+ return regexp.MustCompile(`\s*,\s*`).Split(mime, -1)
+ }
+ return nil
+ }(c.String("mimetype")),
}
return p
}
@@ -382,10 +393,11 @@ func (a *AuthContainer) defDownloadContainer(c *cli.Context) *utl.FileInf {
// DefUploadContainer : Struct container for uploading files
func (a *AuthContainer) defUploadContainer(c *cli.Context) *utl.FileInf {
p := &utl.FileInf{
- Msgar: a.Msg,
- Accesstoken: a.GgsrunCfg.Accesstoken,
- Workdir: a.InitVal.workdir,
- PstartTime: a.InitVal.pstart,
+ Msgar: a.Msg,
+ Accesstoken: a.GgsrunCfg.Accesstoken,
+ Workdir: a.InitVal.workdir,
+ PstartTime: a.InitVal.pstart,
+ UseServiceAccount: a.InitVal.useServiceAccount,
ChunkSize: func(chnk int64) int64 {
if chnk < 1 {
return 1048576
@@ -469,3 +481,20 @@ func (e *ExecutionContainer) adaptProjectForAppsScriptApi() *ExecutionContainer
}
return e
}
+
+// defPermissionsContainer : Struct container for managing permissions
+func (a *AuthContainer) defPermissionsContainer(c *cli.Context) *utl.FileInf {
+ p := a.defDownloadContainer(c)
+ p.PermissionInfo.FileID = c.String("fileid")
+ p.PermissionInfo.PermissionID = c.String("permissionid")
+ p.PermissionInfo.Role = c.String("role")
+ p.PermissionInfo.Type = c.String("type")
+ p.PermissionInfo.Emailaddress = c.String("emailaddress")
+ p.PermissionInfo.Transferownership = c.Bool("transferownership")
+ p.PermissionInfo.CreateObject = c.String("createbyobject")
+ p.PermissionInfo.DeleteObject = c.String("deletebyobject")
+ p.PermissionInfo.UpdateObject = c.String("updatebyobject")
+ p.PermissionInfo.Create = c.Bool("create")
+ p.PermissionInfo.Delete = c.Bool("delete")
+ return p
+}
diff --git a/oauth.go b/oauth.go
index 40d2480..471e8e5 100644
--- a/oauth.go
+++ b/oauth.go
@@ -19,10 +19,19 @@ import (
"time"
"github.com/tanaikech/ggsrun/utl"
+ gettokenbyserviceaccount "github.com/tanaikech/go-gettokenbyserviceaccount"
)
// Goauth :
func (a *AuthContainer) goauth() *AuthContainer {
+ if a.useServiceAccount != "" {
+ if err := a.getAtFromSa(); err != nil {
+ fmt.Fprintf(os.Stderr, "Error: %s\n", err)
+ os.Exit(1)
+ }
+ a.Msg = append(a.Msg, "Service Account was used.")
+ return a
+ }
if len(a.GgsrunCfg.Clientid) > 0 &&
len(a.GgsrunCfg.Clientsecret) > 0 &&
len(a.GgsrunCfg.Refreshtoken) > 0 {
@@ -37,6 +46,7 @@ func (a *AuthContainer) goauth() *AuthContainer {
} else {
a.readClientSecret().getNewAccesstoken().makecfgfile()
}
+ a.Msg = append(a.Msg, "Access Token was was used.")
return a
}
@@ -263,3 +273,23 @@ func (a *AuthContainer) getNewAccesstoken() *AuthContainer {
a.GgsrunCfg.Expiresin = a.chkAtoken() - 360 // 6 minutes as adjustment time
return a
}
+
+// getAtFromSa : Retrieve access token from Service Account
+func (a *AuthContainer) getAtFromSa() error {
+ credentialsData, err := ioutil.ReadFile(a.useServiceAccount)
+ if err != nil {
+ return err
+ }
+ para := struct {
+ PrivateKey string `json:"private_key"`
+ ClientEmail string `json:"client_email"`
+ }{}
+ json.Unmarshal(credentialsData, ¶)
+ scopes := strings.Join(a.Scopes, " ")
+ res, err := gettokenbyserviceaccount.Do(para.PrivateKey, para.ClientEmail, scopes)
+ if err != nil {
+ return err
+ }
+ a.GgsrunCfg.Accesstoken = res.AccessToken
+ return nil
+}
diff --git a/utl/dlfolders.go b/utl/dlfolders.go
index 711f565..186c22c 100644
--- a/utl/dlfolders.go
+++ b/utl/dlfolders.go
@@ -265,8 +265,19 @@ func (p *FileInf) getFilesFromFolder(folderTree *folderTree) *fileListDl {
f.SearchedFolder.CreatedTime = p.CreatedTime
f.SearchedFolder.ModifiedTime = p.ModifiedTime
f.FolderTree = folderTree
+ var mType string
+ if len(p.InputtedMimeType) > 0 {
+ for i, e := range p.InputtedMimeType {
+ if i == 0 {
+ mType = " and (mimeType='" + e + "'"
+ continue
+ }
+ mType += " or mimeType='" + e + "'"
+ }
+ mType += ")"
+ }
for i, id := range folderTree.Folders {
- q := "'" + id + "' in parents and mimeType != 'application/vnd.google-apps.folder' and trashed=false"
+ q := "'" + id + "' in parents and mimeType != 'application/vnd.google-apps.folder' and trashed=false" + mType
fields := "files(createdTime,description,id,mimeType,modifiedTime,name,owners,parents,permissions,shared,size,webContentLink,webViewLink),nextPageToken"
fm := p.GetListLoop(q, fields)
var fe fileListEle
@@ -388,10 +399,13 @@ func (p *FileInf) getOwner() bool {
// DlFolders : Main method for downloading folders
func (p *FileInf) DlFolders() error {
if p.Progress {
- fmt.Printf("Files are downloaded from a folder '%s'.\n", p.FileName)
+ if p.ShowFileInf {
+ fmt.Printf("File finformation is retrieved from a folder '%s'.\n", p.FileName)
+ } else {
+ fmt.Printf("Files are downloaded from a folder '%s'.\n", p.FileName)
+ }
fmt.Println("Getting values to download.")
}
- p.Msgar = append(p.Msgar, fmt.Sprintf("Files were downloaded from folder '%s'.", p.FileName))
folT := &folderTree{}
if p.getOwner() {
q := "mimeType='application/vnd.google-apps.folder' and trashed=false"
@@ -407,8 +421,10 @@ func (p *FileInf) DlFolders() error {
p.dupChkFoldersFiles(fileList)
if p.ShowFileInf {
p.FolderTree = fileList
+ p.Msgar = append(p.Msgar, fmt.Sprintf("File list in folder '%s' was retrieved.", p.FileName))
return nil
}
+ p.Msgar = append(p.Msgar, fmt.Sprintf("Files were downloaded from folder '%s'.", p.FileName))
dlres := p.initDownload(fileList)
if dlres != nil {
return dlres
diff --git a/utl/getdriveinformation.go b/utl/getdriveinformation.go
new file mode 100644
index 0000000..bbb26e2
--- /dev/null
+++ b/utl/getdriveinformation.go
@@ -0,0 +1,40 @@
+// Package utl (getdriveinformation.go) :
+// These method is for retrieving Drive Information.
+package utl
+
+import (
+ "fmt"
+ "net/url"
+ "os"
+ "path"
+)
+
+// getDriveInf : Get drive information using Drive API.
+func (p *FileInf) getDriveInf() error {
+ u, err := url.Parse(driveapiv3)
+ if err != nil {
+ return err
+ }
+ u.Path = path.Join(u.Path, "about")
+ q := u.Query()
+ q.Set("fields", p.SearchFields)
+ u.RawQuery = q.Encode()
+ r := &RequestParams{
+ Method: "GET",
+ APIURL: u.String(),
+ Data: nil,
+ Accesstoken: p.Accesstoken,
+ Dtime: 30,
+ }
+ p.reqAndGetRawResponse(r)
+ return nil
+}
+
+// GetDriveInformation : Get Drive Information.
+func (p *FileInf) GetDriveInformation() *FileInf {
+ if err := p.getDriveInf(); err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+ return p
+}
diff --git a/utl/managepermissions.go b/utl/managepermissions.go
new file mode 100644
index 0000000..5919d70
--- /dev/null
+++ b/utl/managepermissions.go
@@ -0,0 +1,152 @@
+// Package utl (managepermissions.go) :
+// These methods are for managing permissions of files and folders in Google Drive.
+package utl
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "net/url"
+ "os"
+ "path"
+)
+
+// permissionInf : Struct for permission information.
+type permissionInf struct {
+ FileID string `json:"fileId,omitempty"`
+ PermissionID string `json:"permissionId,omitempty"`
+ Role string `json:"role,omitempty"`
+ Type string `json:"type,omitempty"`
+ Emailaddress string `json:"emailaddress,omitempty"`
+ Transferownership bool `json:"transferownership,omitempty"`
+ CreateObject string `json:"createRequestBody,omitempty"`
+ DeleteObject string `json:"deleteRequestBody,omitempty"`
+ UpdateObject string `json:"updateRequestBody,omitempty"`
+ Create bool `json:"-"`
+ Delete bool `json:"-"`
+}
+
+// getPermissions : Retrieve permissions.
+func (p *FileInf) getPermissions(u *url.URL) error {
+ u.Path = path.Join(u.Path, p.PermissionInfo.PermissionID)
+ q := u.Query()
+ q.Set("fields", "allowFileDiscovery,deleted,displayName,domain,emailAddress,expirationTime,id,kind,photoLink,role,teamDrivePermissionDetails,type")
+ u.RawQuery = q.Encode()
+ r := &RequestParams{
+ Method: "GET",
+ APIURL: u.String(),
+ Data: nil,
+ Accesstoken: p.Accesstoken,
+ Dtime: 30,
+ }
+ p.reqAndGetRawResponse(r)
+ return nil
+}
+
+// getPermissionsList : Retrieve permissions list.
+func (p *FileInf) getPermissionsList(u *url.URL) error {
+ q := u.Query()
+ q.Set("pageSize", "100")
+ q.Set("fields", "kind,nextPageToken,permissions")
+ u.RawQuery = q.Encode()
+ r := &RequestParams{
+ Method: "GET",
+ APIURL: u.String(),
+ Data: nil,
+ Accesstoken: p.Accesstoken,
+ Dtime: 30,
+ }
+ p.reqAndGetRawResponse(r)
+ return nil
+}
+
+// deletePermissions : Delete permissions.
+func (p *FileInf) deletePermissions(u *url.URL) error {
+ u.Path = path.Join(u.Path, p.PermissionInfo.PermissionID)
+ r := &RequestParams{
+ Method: "DELETE",
+ APIURL: u.String(),
+ Accesstoken: p.Accesstoken,
+ Dtime: 30,
+ }
+ _, err := r.FetchAPIRaw()
+ if err != nil {
+ return fmt.Errorf("Permission of '%s' was NOT deleted. Please confirm the fileId and permissionId again", p.PermissionInfo.PermissionID)
+ }
+ p.Msgar = append(p.Msgar, fmt.Sprintf("Permission of '%s' was deleted.", p.PermissionInfo.PermissionID))
+ return nil
+}
+
+// createPermissions : Create permissions.
+func (p *FileInf) createPermissions(u *url.URL) error {
+ q := u.Query()
+ if p.PermissionInfo.Role == "" || p.PermissionInfo.Type == "" {
+ return fmt.Errorf("role and type are required for creating permissions")
+ }
+ if p.PermissionInfo.Transferownership {
+ q.Set("transferOwnership", "true")
+ }
+ q.Set("fields", "allowFileDiscovery,deleted,displayName,domain,emailAddress,expirationTime,id,kind,photoLink,role,teamDrivePermissionDetails,type")
+ u.RawQuery = q.Encode()
+ meta := struct {
+ Role string `json:"role,omitempty"`
+ Type string `json:"type,omitempty"`
+ EmailAddress string `json:"emailAddress,omitempty"`
+ }{
+ p.PermissionInfo.Role, p.PermissionInfo.Type, p.PermissionInfo.Emailaddress,
+ }
+ metadata, err := json.Marshal(meta)
+ if err != nil {
+ return err
+ }
+ r := &RequestParams{
+ Method: "POST",
+ APIURL: u.String(),
+ Data: bytes.NewBuffer(metadata),
+ Accesstoken: p.Accesstoken,
+ Contenttype: "application/json",
+ Dtime: 30,
+ }
+ p.reqAndGetRawResponse(r)
+ p.Msgar = append(p.Msgar, "Permission was created.")
+ return nil
+}
+
+// getURL : Get URL for fetching Permissions.
+func (p *FileInf) getURL() (*url.URL, error) {
+ u, err := url.Parse(driveapiurl)
+ if err != nil {
+ return nil, err
+ }
+ u.Path = path.Join(u.Path, p.PermissionInfo.FileID)
+ u.Path = path.Join(u.Path, "permissions")
+ return u, nil
+}
+
+// ManagePermissions : Main method of Manage Permissions.
+func (p *FileInf) ManagePermissions() *FileInf {
+ var err error
+ u, err := p.getURL()
+ if p.PermissionInfo.FileID != "" {
+ if p.PermissionInfo.PermissionID == "" {
+ if (p.PermissionInfo.Create && !p.PermissionInfo.Delete) || (p.PermissionInfo.CreateObject != "" && p.PermissionInfo.DeleteObject == "" && p.PermissionInfo.UpdateObject == "") {
+ err = p.createPermissions(u)
+ } else {
+ err = p.getPermissionsList(u)
+ }
+ } else {
+ if (!p.PermissionInfo.Create && p.PermissionInfo.Delete) || (p.PermissionInfo.CreateObject == "" && p.PermissionInfo.DeleteObject != "" && p.PermissionInfo.UpdateObject == "") {
+ err = p.deletePermissions(u)
+ } else {
+ err = p.getPermissions(u)
+ }
+ }
+ } else {
+ err = fmt.Errorf("Invalid options. Please check HELP using $ ggsrun p --help")
+ }
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+ return p
+}
diff --git a/utl/transfer.go b/utl/transfer.go
index 5123e35..d225787 100644
--- a/utl/transfer.go
+++ b/utl/transfer.go
@@ -24,6 +24,7 @@ import (
)
const (
+ driveapiv3 = "https://www.googleapis.com/drive/v3"
lurl = "https://www.googleapis.com/drive/v3/files?"
driveapiurl = "https://www.googleapis.com/drive/v3/files/"
driveapiurlv2 = "https://www.googleapis.com/drive/v2/files/"
@@ -60,27 +61,31 @@ type FileInf struct {
SearchRegex string `json:"regex,omitempty"`
SearchedFiles []fileS `json:"searchedFiles,omitempty"`
SearchedResult string `json:"searchedResult,omitempty"`
+ InputtedMimeType []string `json:"inputtedMimeType,omitempty"`
+ ReturnedResult interface{} `json:"returnedResult,omitempty"`
- Accesstoken string `json:"-"`
- BoundScriptName string `json:"-"`
- ChunkSize int64 `json:"-"`
- ConvertTo string `json:"-"`
- DlMime string `json:"-"`
- GoogleDocName string `json:"-"`
- OverWrite bool `json:"-"`
- Progress bool `json:"-"`
- ProjectType string `json:"-"`
- PstartTime time.Time `json:"-"`
- RawProject bool `json:"-"`
- SearchByName string `json:"-"`
- SearchByID string `json:"-"`
- ShowFileInf bool `json:"-"`
- Size int64 `json:"-"`
- Skip bool `json:"-"`
- WantExt string `json:"-"`
- WantName string `json:"-"`
- Workdir string `json:"-"`
- Zip bool `json:"-"`
+ Accesstoken string `json:"-"`
+ BoundScriptName string `json:"-"`
+ ChunkSize int64 `json:"-"`
+ ConvertTo string `json:"-"`
+ DlMime string `json:"-"`
+ GoogleDocName string `json:"-"`
+ OverWrite bool `json:"-"`
+ PermissionInfo permissionInf `json:"-"`
+ Progress bool `json:"-"`
+ ProjectType string `json:"-"`
+ PstartTime time.Time `json:"-"`
+ RawProject bool `json:"-"`
+ SearchByName string `json:"-"`
+ SearchByID string `json:"-"`
+ ShowFileInf bool `json:"-"`
+ Size int64 `json:"-"`
+ Skip bool `json:"-"`
+ UseServiceAccount string `json:"-"`
+ WantExt string `json:"-"`
+ WantName string `json:"-"`
+ Workdir string `json:"-"`
+ Zip bool `json:"-"`
}
// owners : Owners of file
@@ -799,6 +804,9 @@ func (p *FileInf) Uploader(c *cli.Context) *FileInf {
}(c.Bool("noconvert")),
}
if metadata.MimeType == "application/vnd.google-apps.script" {
+ if p.UseServiceAccount != "" {
+ return p.whenServiceAccountIsUsed()
+ }
var pr project
filedata := &filea{
Name: metadata.Name,
@@ -827,6 +835,9 @@ func (p *FileInf) Uploader(c *cli.Context) *FileInf {
}
}
} else {
+ if p.UseServiceAccount != "" {
+ return p.whenServiceAccountIsUsed()
+ }
if p.ParentID == "" {
if p.ProjectType == "standalone" {
metadata := &fileUploaderMeta{
@@ -1142,3 +1153,26 @@ func (p *FileInf) getList(ptoken, q, fields string) ([]byte, error) {
body, err := r.FetchAPI()
return body, err
}
+
+// whenServiceAccountIsUsed : When ServiceAccount is used, there are some limitations.
+func (p *FileInf) whenServiceAccountIsUsed() *FileInf {
+ p.Msgar = append(p.Msgar, fmt.Sprintf("Warning: In the current stage, script files cannot be uploaded and downloaded by Service Account yet."))
+ return p
+}
+
+// saveResponse : Save response from API.
+func (p *FileInf) saveResponse(body []byte) {
+ var rm interface{}
+ json.Unmarshal(body, &rm)
+ p.ReturnedResult = rm
+}
+
+// reqAndGetRawResponse : Request and retrieve raw resource.
+func (p *FileInf) reqAndGetRawResponse(r *RequestParams) {
+ res, err := r.FetchAPI()
+ if err != nil {
+ p.errHandlingFromFetch(res)
+ return
+ }
+ p.saveResponse(res)
+}