Skip to content

Commit

Permalink
appengine-client example
Browse files Browse the repository at this point in the history
  • Loading branch information
battis committed Mar 14, 2024
1 parent 811e3e4 commit 360f52e
Show file tree
Hide file tree
Showing 17 changed files with 2,638 additions and 22 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
*.cache
/.cache/
/coverage/
node_modules
/tools/
/var/
/vendor/
5 changes: 3 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"battis/data-utilities": "^1.2",
"battis/lazy-secrets": "^1.1",
"battis/openapi-client-generator": "^0.1",
"groton-school/sky-api.appengine-client": "^0.2",
"guzzlehttp/guzzle": "^7.4",
"league/oauth2-client": "^2.6"
},
Expand Down Expand Up @@ -61,9 +62,9 @@
"groton-school/sky-api-school": "self.version",
"groton-school/sky-api.altru-analysis": "self.version",
"groton-school/sky-api.altru-constituent": "self.version",
"groton-school/sky-api.appengine-client-example": "self.version",
"groton-school/sky-api.nxt-data-integration": "self.version",
"groton-school/sky-api.oneroster": "self.version",
"groton-school/sky-api.school": "self.version"
},
"minimum-stability": "dev"
}
}
2 changes: 2 additions & 0 deletions examples/appengine-client/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.env
/vendor/
10 changes: 10 additions & 0 deletions examples/appengine-client/bin/deploy.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/usr/bin/env node
import gcloud from '@battis/partly-gcloudy';

await gcloud.init();
try {
await gcloud.batch.appEngineDeployAndCleanup({ retainVersions: 2 });
console.log('Deploy complete.');
} catch (e) {
console.log.error(e);
}
29 changes: 29 additions & 0 deletions examples/appengine-client/bin/options.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
export default {
name: {
description: "Google Cloud project name",
default: "Blackbaud SKY API AppEngine Client Example",
},
billing: {
description: "Google Cloud billing account ID for this project",
},
region: {
description: "Google Cloud region in which to create App Engine instance",
},
supportEmail: {
description: "Support email address for app OAuth consent screen",
},
users: {
description: "Google IDs of users allowed to access app (comma-separated)",
},
accessKey: {
description: "Blackbaud SKY API subscription access key",
url: "https://developer.blackbaud.com/subscriptions",
},
clientId: {
description: "Blackbaud SKY API app OAuth client ID",
url: "https://developer.blackbaud.com/apps",
},
clientSecret: {
description: "Blackbaud SKY API app OAuth client secret",
},
};
135 changes: 135 additions & 0 deletions examples/appengine-client/bin/setup.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
#!/usr/bin/env node
import options from './options.mjs';
import gcloud from '@battis/partly-gcloudy';
import cli from '@battis/qui-cli';
import fs from 'fs';

async function guideBlackbaudAppCreation({
url,
accessKey = undefined,
clientId = undefined,
clientSecret = undefined
}) {
accessKey = await cli.prompts.input({
message: `${options.accessKey.description} from ${cli.colors.url(
options.accessKey.url
)}`,
validate: cli.validators.notEmpty,
default: accessKey
});
await cli.prompts.confirm({
message: `Create a new app at ${cli.colors.url(options.clientId.url)}`
});
clientId = await cli.prompts.input({
message: options.clientId.description,
validate: cli.validators.notEmpty,
default: clientId
});
clientSecret = await cli.prompts.input({
message: options.clientSecret.description,
validate: cli.validators.notEmpty,
default: clientSecret
});
const redirectUrl = `${url}/redirect`;
await cli.prompts.confirm({
message: `Configure ${cli.colors.value(
redirectUrl
)} as the app's redirect URL`
});
await cli.prompts.confirm({
message: `Limit the SKY API scopes as described at ${cli.colors.url(
'https://github.com/groton-school/blackbaud-to-google-group-sync/blob/main/docs/blackbaud-api-scope.md'
)}`
});
return { accessKey, clientId, clientSecret, redirectUrl };
}

(async () => {
const args = await gcloud.init({
args: {
options,
flags: {
deploy: {
description:
'Include the (time-consuming) deploy step to App Engine (default true, --no-deploy to skip)'
}
}
}
});
if (args.values.verbose) {
cli.shell.setSilent(false);
}

if (gcloud.ready()) {
const { project, appEngine } = await gcloud.batch.appEnginePublish({
name: args.values.name,
id: args.values.project,
suggestedName: 'Blackbaud SKY API AppEngine Client Example',
billingAccountId: args.values.billing,
region: args.values.region,
env: { keys: { urlVar: 'URL' } },
prebuild: () => {
return true;
},
deploy: args.values.deploy
});

// must create an instance so that IAP can be configured
if (!args.values.built) {
cli.shell.exec('pnpm run build');
}
if (!args.values.deployed) {
cli.shell.exec('pnpm run deploy');
}

// enable IAP to limit access to app
await gcloud.iap.enable({
applicationTitle: project.name,
supportEmail: args.values.supportEmail,
users: args.values.users
});

// guide storage of Blackbaud credentials in Secret Manager, SKY App creation and configuration
const blackbaud = await guideBlackbaudAppCreation({
url,
accessKey: args.values.accessKey,
clientId: args.values.clientId,
clientSecret: args.values.clientSecret
});
await gcloud.secrets.set({
name: 'BLACKBAUD_ACCESS_KEY',
value: blackbaud.accessKey
});
await gcloud.secrets.set({ name: 'BLACKBAUD_API_TOKEN', value: 'null' });
await gcloud.secrets.set({
name: 'BLACKBAUD_CLIENT_ID',
value: blackbaud.clientId
});
await gcloud.secrets.set({
name: 'BLACKBAUD_CLIENT_SECRET',
value: blackbaud.clientSecret
});
await gcloud.secrets.set({
name: 'BLACKBAUD_REDIRECT_URL',
value: blackbaud.redirectUrl
});
await gcloud.secrets.set({
name: 'GOOGLE_DELEGATED_ADMIN',
value: args.values.delegatedAdmin
});
await gcloud.secrets.set({
name: 'GOOGLE_CREDENTIALS',
path: credentialsPath
});
fs.unlinkSync(credentialsPath);
await gcloud.secrets.enableAppEngineAccess();

// authorize the app to use Blackbaud SKY API manually
await cli.prompts.confirm({
message: `Confirm that you have authorized the app at ${cli.colors.url(
url
)}`
});

}
})();
22 changes: 22 additions & 0 deletions examples/appengine-client/composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "groton-school/sky-api.appengine-client-example",
"description": "Blackbaud SKY API AppEngine Client Example",
"license": "GPL-3.0",
"authors": [
{
"name": "Seth Battis",
"email": "[email protected]"
}
],
"require": {
"groton-school/sky-api.appengine-client": "^0.2",
"groton-school/sky-api.school": "^0.2"
},
"scripts": {
"setup": [
"pnpm install",
"bin/setup.mjs"
],
"deploy": "bin/deploy.mjs"
}
}
1 change: 1 addition & 0 deletions examples/appengine-client/example.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
PROJECT=google-cloud-projectId
6 changes: 6 additions & 0 deletions examples/appengine-client/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"devDependencies": {
"@battis/partly-gcloudy": "^0.4.0",
"@battis/qui-cli": "^0.4.0"
}
}
29 changes: 29 additions & 0 deletions examples/appengine-client/public/index.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

use GrotonSchool\SKY\AppEngine\ClientFactory;
use Blackbaud\SKY\School\Client;

require_once __DIR__ . '../vendor/autoload.php';

$factory = new ClientFactory();

/** @var \Blackbaud\SKY\School\Client $client */
$client = $factory->get(Client::class);

$me = $client->v1->users->me->getAllBy();
$json = json_encode($me, JSON_PRETTY_PRINT);

echo <<<EOT
<!DOCTYPE html>
<html lang="en">
<head>
<title>Me</title>
</head>
<body>
<h1>{$me->first_name} {$me->last_name}</h1>
<pre lang="json">
{$json}
</pre>
</body>
<html>
EOT;
14 changes: 14 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"workspaces": [
"packages/*",
"examples/*"
],
"devDependencies": {
"@battis/eslint-config": "^1.1.2",
"@battis/prettier-config": "^1.3.0"
},
"prettier": "@battis/prettier-config",
"eslint": {
"extends": "@battis/eslint-config"
}
}
1 change: 0 additions & 1 deletion packages/appengine-client/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
"name": "battis/sky-api-appengine",
"description": "Blackbaud SKY API PHP client native to Google App Engine",
"license": "GPL-3.0",
"minimum-stability": "dev",
"authors": [
{
"name": "Seth Battis",
Expand Down
7 changes: 3 additions & 4 deletions packages/appengine-client/src/ClientFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
use Battis\LazySecrets\Cache;
use Battis\OpenAPI\Client\BaseEndpoint;
use Battis\OpenAPI\Client\Client;
use Blackbaud\SKY\School\Client as SchoolClient;
use GrotonSchool\OAuth2\Client\Provider\BlackbaudSKY;

/**
Expand All @@ -21,16 +20,16 @@ class ClientFactory
private Cache $secrets;
private TokenStorage $storage;

public function __construct(?string $projectId)
public function __construct(?string $projectId = null)
{
$this->secrets = new Cache($projectId ?? $_ENV["GOOGLE_CLOUD_PROJECT"]);
$this->storage = new TokenStorage($this->secrets);
}

/**
* @param class-string<BaseEndpoint> $class
* @param class-string<\Battis\OpenAPI\Client\BaseEndpoint> $class
*
* @return BaseEndpoint
* @return \Battis\OpenAPI\Client\BaseEndpoint
*
* @psalm-suppress UnsafeInstantiation
*/
Expand Down
2 changes: 1 addition & 1 deletion packages/appengine-client/src/TokenStorage.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class TokenStorage extends ClientTokenStorage
private Cache $secrets;

/**
* @param ?string|Cache $projectId
* @param ?string|\Battis\LazySecrets\Cache $projectId
*/
public function __construct(mixed $projectId)
{
Expand Down
14 changes: 0 additions & 14 deletions packages/oauth2-example/composer.json

This file was deleted.

Loading

0 comments on commit 360f52e

Please sign in to comment.