Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .htaccess
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ RewriteRule "^keyboard/(.*)$" "/script/keyboard/keyboard.php?id=$1" [END]

RewriteRule "^increment-download/(.*)$" "/script/increment-download/increment-download.php?id=$1" [END]

#### Rewrites for /script folder: /app-downloads-increment

RewriteRule "^app-downloads-increment/([^/]+)/([^/]+)/(.*)$" "/script/app-downloads-increment/app-downloads-increment.php?product=$1&version=$2&tier=$3" [END]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the internal key get passed here too?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a POST endpoint and so the key is passed as POST formdata to the .php, not as part of the URL.


#### Rewrites for /script folder: /model

RewriteRule "^model(/)?$" "/script/model-search/model-search.php" [END]
Expand Down
23 changes: 23 additions & 0 deletions schemas/app-downloads-increment/1.0/app-downloads-increment.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"$schema": "http://json-schema.org/draft-07/schema",
"$ref": "#/definitions/app-downloads-increment",

"definitions": {
"app-downloads-increment": {
"type": "object",
"required": [
"product",
"version",
"tier",
"count"
],
"additionalProperties": true,
"properties": {
"product": { "type": "string" },
"version": { "type": "string" },
"tier": { "type": "string" },
"count": { "type": "integer" }
}
}
}
}
28 changes: 28 additions & 0 deletions script/app-downloads-increment/app-downloads-increment.inc.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php
namespace Keyman\Site\com\keyman\api;

require_once(__DIR__ . '/../../tools/util.php');

class AppDownloads {
static function increment($mssql, $product, $version, $tier) {

$stmt = $mssql->prepare('EXEC sp_app_downloads_increment :product, :version, :tier');
$stmt->bindParam(":product", $product);
$stmt->bindParam(":version", $version);
$stmt->bindParam(":tier", $tier);
$stmt->execute();
$data = $stmt->fetchAll();
if(count($data) == 0) {
return NULL;
}

$obj = [
'product' => $data[0]['product'],
'version' => $data[0]['version'],
'tier' => $data[0]['tier'],
'count' => intval($data[0]['count'])
];

return $obj;
}
}
72 changes: 72 additions & 0 deletions script/app-downloads-increment/app-downloads-increment.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php
require_once(__DIR__ . '/../../tools/util.php');

allow_cors();
json_response();

require_once(__DIR__ . '/app-downloads-increment.inc.php');
require_once(__DIR__ . '/../../tools/db/db.php');
require_once __DIR__ . '/../../tools/autoload.php';

use Keyman\Site\Common\KeymanHosts;

$mssql = Keyman\Site\com\keyman\api\Tools\DB\DBConnect::Connect();
$env = getenv();

header('Link: <' . KeymanHosts::Instance()->api_keyman_com .'/schemas/app-downloads-increment/1.0/app-downloads-increment.json#>; rel="describedby"');

$AllowGet = isset($_REQUEST['debug']);

if(!$AllowGet && $_SERVER['REQUEST_METHOD'] != 'POST') {
fail('POST required');
}

if(!isset($_REQUEST['key'])) {
fail('key parameter must be set');
}

// Note: we don't currently unit-test this one
if(KeymanHosts::Instance()->Tier() === KeymanHosts::TIER_DEVELOPMENT)
$key = 'local';
else
$key = $env['API_KEYMAN_COM_INCREMENT_DOWNLOAD_KEY'];

if($_REQUEST['key'] !== $key) {
fail('Invalid key');
}

if(!isset($_REQUEST['product']) ||
!isset($_REQUEST['version']) ||
!isset($_REQUEST['tier'])
) {
// We don't constrain what the product / version / tier may be here, because
// we may add other products in the future
fail('product, version, tier parameters must be set');
}

$product = $_REQUEST['product'];
$version = $_REQUEST['version'];
$tier = $_REQUEST['tier'];

/**
* POST https://api.keyman.com/app-downloads-increment/product/version/tier
*
* Increments the download counter for a single product identified by
* `product`, `version`, and `tier`. Returns the new total count for the
* product/version/tier for the day
*
* https://api.keyman.com/schemas/app-downloads-increment.json is JSON schema
*
* @param product the name of the product to increment ( "android", "ios",
* "linux", "macos", "web", "windows", "developer"...)
* @param version the version number ("1.2.3")
* @param tier the tier of the product ("alpha", "beta", "stable")
* @param key internal key to allow endpoint to run
*/

$json = \Keyman\Site\com\keyman\api\AppDownloads::increment($mssql, $product, $version, $tier);
if($json === NULL) {
fail("Failed to increment stat, invalid parameters [$product, $version, $tier]?", 401);
}

echo json_encode($json, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
20 changes: 19 additions & 1 deletion tools/db/build/search.sql
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,10 @@ CREATE TABLE t_dbdatasources (
DROP TABLE IF EXISTS t_keyboard_downloads;
GO

--add a new schema for kstats here so we can use it in search.sql
-- tables in the kstats schema are persistent on the production
-- infrastructure, unlike the other tables in the database, which
-- are recreated on each deployment.

IF SCHEMA_ID('kstats') IS NULL
BEGIN
EXEC sp_executesql N'CREATE SCHEMA kstats'
Expand All @@ -325,3 +328,18 @@ BEGIN
keyboard_id, statdate
) INCLUDE (count)
END

IF OBJECT_ID('kstats.t_app_downloads', 'U') IS NULL
BEGIN
CREATE TABLE kstats.t_app_downloads (
product NVARCHAR(64) NOT NULL, -- "android", "ios", "linux", "macos", "web", "windows", "developer"...
version NVARCHAR(64) NOT NULL, -- "123.456.789"
tier NVARCHAR(16) NOT NULL, -- "alpha", "beta", "stable"
statdate DATE,
count INT NOT NULL
)

CREATE INDEX ix_app_downloads ON kstats.t_app_downloads (
product, version, tier, statdate
) INCLUDE (count)
END
39 changes: 39 additions & 0 deletions tools/db/build/sp_app_downloads_increment.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
sp_app_downloads_increment
*/

DROP PROCEDURE IF EXISTS sp_app_downloads_increment;
GO

CREATE PROCEDURE sp_app_downloads_increment (
@prmProduct NVARCHAR(64),
@prmVersion NVARCHAR(64),
@prmTier NVARCHAR(16)
) AS
BEGIN
SET NOCOUNT ON;

BEGIN TRANSACTION;

DECLARE @date DATE, @count INT;

SET @date = CONVERT(date, GETDATE());

UPDATE kstats.t_app_downloads
WITH (UPDLOCK, SERIALIZABLE) -- ensure that this statement is atomic with following INSERT
SET count = count + 1, @count = count + 1
WHERE product = @prmProduct AND version = @prmVersion AND tier = @prmTier AND statdate = @date;

IF @@ROWCOUNT = 0
BEGIN
INSERT kstats.t_app_downloads (product, version, tier, statdate, count)
SELECT @prmProduct, @prmVersion, @prmTier, @date, 1
SET @count = 1
END

SET NOCOUNT OFF

SELECT @prmProduct product, @prmVersion version, @prmTier tier, @count count

COMMIT TRANSACTION;
END
1 change: 1 addition & 0 deletions tools/db/build/sp_increment_download.sql
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
sp_increment_download
TODO: rename to sp_keyboard_downloads_increment
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lowpri but more 'namespaced' in the future

*/

DROP PROCEDURE IF EXISTS sp_increment_download;
Expand Down