SCRT-HQ / PSGSuite

Powershell module for Google / G Suite API calls wrapped in handy functions. Authentication is established using a service account via P12 key to negate the consent popup and allow for greater handsoff automation capabilities
https://psgsuite.io/
Apache License 2.0
234 stars 66 forks source link

Add Presentation Support #282

Closed FISHMANPET closed 4 years ago

FISHMANPET commented 4 years ago

Implements getting and editing a Presentation

I needed it, so I wrote it. This partially implements #150. It's not the full Presentations API. It's essentially just presentations.batchUpdate and presentations.get, leaving presentations.create, presentations.pages.get, and presentations.pages.getThumbnail to implement.

This also ended up being fairly low level, basically just serving as a wrapper around the API. Unlike some of the other PSGSuite functions, this requires a fair amount of knowledge of how the API functions.

Three new functions:

Get-GSPresentation

This is pretty self-explanatory, it gets the presentation object, raw (it's not doing anything like the GSheet commands that translate the sheet into a data structure useful in PowerShell. I don't think any kind of transformation would be universally useful, so getting the object is the best we can do.

Edit-GSPresentation

This is what will make the edit to the slide. It takes a set of Updates (either as an array passed to the parameter, or via Pipeline) and will execute them against the given presentations. This uses batchUpdate, and when multiple Updates are sent as a single request, they are completed atomically. While in other places this module uses batchUpdate to do a single update, I thought the atomicity was valuable here so I wrote this function such that it would take advantage of that fact. But what is an Update you ask? Well...

New-GSPresentationUpdateRequest

Updates are made with Requests. Specifically there's a Requests object, and each Requests object has over 40 properties, for each type of Request. A Request can only have one of these Requests specified. Then the batchUpdate submits all the Requests at once. To generate a request, you have to understand the API to know how to make your change. I'll use CreateSlide as an example, because it's the simples.

CreateSlide (like all request types) request a CreateSlideRequest. Once that CreateSlideRequest is created, it is attached to the Request. This will validate that request you're creating exists, using Dynamic Parameters. It means you can tab-complete through all the request types, and if a new request type is added, it will automatically be supported.

The properties for a CreateSlideRequest are simple, and can actually work with no properties, but it still requires an empty hashtable. So to generate an update request for a new blank slide at the end of a presentation (any presentation, the request is not necessarily tied to a specific Spreadsheet) I'd run this command:

$newSlide = New-GSPresentationUpdateRequest -RequestType CreateSlide -RequestProperties @{}

This can then be used with Edit-GSPresentation to actually make the change:

$result = Edit-GSPresentation -PresentationId $ID -Update $newslide

Some of the request types require arrays of data (even for a single input) and the library expects them to be Generic Lists. Instead of requiring the user to know what type of Generic List to construct and how to construct a Generic List, I've added code to this that will do it for you, so for something like moving a slide with UpdateSlidesPosition you would create the request like this:

$moveSlideProperties = @{slideObjectIds = @($slideToMove.ObjectId); insertionIndex=0}
$moveSlide = New-GSPresentationUpdateRequest -RequestType UpdateSlidesPosition -RequestProperties $moveSlideProperties

On the backend, this will detect if one of the parameters of the request is an array, and if so, make a new Generic List of the type of the first object (all arrays require the same object type) and then inserts each entry into List.

This part of the code abstracts the .Net implementation of the library, making the behavior much more like the API as described in Google's Documentation.


These generic functions let you fully utilize the API to edit presentations, though in a few cases the API requires special .Net objects to make the request. For example, a ReplaceAllText request requires a SubstringMatchCriteria object, which means you need to run something like New-Object Google.Apis.Slides.v1.Data.SubstringMatchCriteria, that part isn't currently abstracted by this code. Additional functions could easily be written that would generate this type of object without needing to know the underlying .Net object names. In most cases these objects a fairly simple, for example creating a SubstringMatchCriteria requires 2 properties, a boolean of case sensitivity and the text to search for that will be replaced.

These generic functions could also be used as backing for more update-specific functions. A New-GSPresentationSlide function with optional parameters objectId, insertionIndex, slideLayoutReference, and a placeholderIdMappings (the last two requiring custom objects, so since those objects aren't used anywhere else but in a new slide request, they can be created all within a single function)

I have been able to use all these myself so they've been tested in that capacity, but I'm not adding any new tests, because I'm not sure what I would be testing here, as you can see looking at the code there's very little "logic" to these functions to test for.

Per discussion in #275 around the User parameter and the pipeline, I've tried to be thoughtful about what can be received over the pipeline. Specifically I've removed the User flag from being received from the pipeline by name. All 3 functions do support pipelining. For Get-GSPresentation you can pipe in presentation Ids, for Edit-GSPresentation you can pipe in your update requests, and for New-GSPresentationUpdateRequest you can pipe in custom objects, each containing a valid RequestType and valid RequestProperties for that RequestType.

I also see this is the first use of the Edit verb in this project, but it seemed far more appropriate than anything like Update or Set for modifying a presentation, but I'm open to other thoughts on that.

scrthq commented 4 years ago

@FISHMANPET This is awesome! I have something similar in the works already for Docs/Sheets with Slides on the visibility plane as well, but I wanted to auto-generate the functions that wrap those other .NET object creation and provide pipeline support.

Giving this a better review once I have time, I sincerely appreciate both the work you've put in here and the patience while I get caught up!

FISHMANPET commented 4 years ago

I'd love to see/help with any auto-generation code. Working directly with .Net objects like PSGsuite does is pretty new to me, so I'd love to learn more about how to do it and contribute.

I'd actually considered expanding the dynamic parameter set on New-GSPresentationUpdateRequest so for reach request type it would auto-determine what the parameters were for that particular request and if they were optional or not, but I'm not sure if the .Net libraries even have enough information to do that. And this was the first dynamic parameter set I'd written so that was new to me as well.