diff --git a/app/config/collections/projects.php b/app/config/collections/projects.php index f9a44e169c..b209a7ec32 100644 --- a/app/config/collections/projects.php +++ b/app/config/collections/projects.php @@ -590,6 +590,50 @@ return [ 'array' => false, 'filters' => ['datetime'], ], + [ + '$id' => ID::custom('latestDeploymentId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('latestDeploymentInternalId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('latestDeploymentCreatedAt'), + 'type' => Database::VAR_DATETIME, + 'format' => '', + 'size' => 0, + 'signed' => false, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['datetime'], + ], + [ + '$id' => ID::custom('latestDeploymentStatus'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16, + 'signed' => true, + 'required' => false, + 'default' => '', + 'array' => false, + 'filters' => [], + ], [ '$id' => ID::custom('vars'), 'type' => Database::VAR_STRING, @@ -1047,6 +1091,50 @@ return [ 'array' => false, 'filters' => [], ], + [ + '$id' => ID::custom('latestDeploymentId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('latestDeploymentInternalId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('latestDeploymentCreatedAt'), + 'type' => Database::VAR_DATETIME, + 'format' => '', + 'size' => 0, + 'signed' => false, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['datetime'], + ], + [ + '$id' => ID::custom('latestDeploymentStatus'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16, + 'signed' => true, + 'required' => false, + 'default' => '', + 'array' => false, + 'filters' => [], + ], [ '$id' => ID::custom('vars'), 'type' => Database::VAR_STRING, diff --git a/app/config/regions.php b/app/config/regions.php index 61029a9996..05e04930fe 100644 --- a/app/config/regions.php +++ b/app/config/regions.php @@ -3,72 +3,8 @@ return [ 'default' => [ '$id' => 'default', - 'name' => 'Frankfurt', + 'name' => 'default', 'disabled' => false, - 'flag' => 'de', - 'default' => true, - ], - 'fra' => [ - '$id' => 'fra', - 'name' => 'Frankfurt', - 'disabled' => false, - 'flag' => 'de', - 'default' => true, - ], - 'nyc' => [ - '$id' => 'nyc', - 'name' => 'New York', - 'disabled' => true, - 'flag' => 'us', - 'default' => true, - ], - 'sfo' => [ - '$id' => 'sfo', - 'name' => 'San Francisco', - 'disabled' => true, - 'flag' => 'us', - 'default' => true, - ], - 'blr' => [ - '$id' => 'blr', - 'name' => 'Bengaluru', - 'disabled' => true, - 'flag' => 'in', - 'default' => true, - ], - 'lon' => [ - '$id' => 'lon', - 'name' => 'London', - 'disabled' => true, - 'flag' => 'gb', - 'default' => true, - ], - 'ams' => [ - '$id' => 'ams', - 'name' => 'Amsterdam', - 'disabled' => true, - 'flag' => 'nl', - 'default' => true, - ], - 'sgp' => [ - '$id' => 'sgp', - 'name' => 'Singapore', - 'disabled' => true, - 'flag' => 'sg', - 'default' => true, - ], - 'tor' => [ - '$id' => 'tor', - 'name' => 'Toronto', - 'disabled' => true, - 'flag' => 'ca', - 'default' => true, - ], - 'syd' => [ - '$id' => 'syd', - 'name' => 'Sydney', - 'disabled' => true, - 'flag' => 'au', 'default' => true, ], ]; diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index 76dd01367e..9e9f99db63 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -10140,7 +10140,7 @@ }, { "name": "queries", - "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: buildSize, sourceSize, buildDuration, status, activate, type", + "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: buildSize, sourceSize, totalSize, buildDuration, status, activate, type", "required": false, "schema": { "type": "array", @@ -19353,8 +19353,7 @@ "description": "Project Region.", "x-example": "default", "enum": [ - "default", - "fra" + "default" ], "x-enum-name": null, "x-enum-keys": [] @@ -25699,7 +25698,7 @@ }, { "name": "queries", - "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: buildSize, sourceSize, buildDuration, status, activate, type", + "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: buildSize, sourceSize, totalSize, buildDuration, status, activate, type", "required": false, "schema": { "type": "array", @@ -36875,6 +36874,36 @@ "description": "Site's active deployment ID.", "x-example": "5e5ea5c16897e" }, + "deploymentCreatedAt": { + "type": "string", + "description": "Active deployment creation date in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "deploymentScreenshotLight": { + "type": "string", + "description": "Screenshot of active deployment with light theme preference file ID.", + "x-example": "5e5ea5c16897e" + }, + "deploymentScreenshotDark": { + "type": "string", + "description": "Screenshot of active deployment with dark theme preference file ID.", + "x-example": "5e5ea5c16897e" + }, + "latestDeploymentId": { + "type": "string", + "description": "Site's latest deployment ID.", + "x-example": "5e5ea5c16897e" + }, + "latestDeploymentCreatedAt": { + "type": "string", + "description": "Latest deployment creation date in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "latestDeploymentStatus": { + "type": "string", + "description": "Status of latest deployment. Possible values are \"waiting\", \"processing\", \"building\", \"ready\", and \"failed\".", + "x-example": "ready" + }, "vars": { "type": "array", "description": "Site variables.", @@ -36959,6 +36988,12 @@ "live", "framework", "deploymentId", + "deploymentCreatedAt", + "deploymentScreenshotLight", + "deploymentScreenshotDark", + "latestDeploymentId", + "latestDeploymentCreatedAt", + "latestDeploymentStatus", "vars", "timeout", "installCommand", @@ -37183,6 +37218,26 @@ "description": "Function's active deployment ID.", "x-example": "5e5ea5c16897e" }, + "deploymentCreatedAt": { + "type": "string", + "description": "Active deployment creation date in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "latestDeploymentId": { + "type": "string", + "description": "Function's latest deployment ID.", + "x-example": "5e5ea5c16897e" + }, + "latestDeploymentCreatedAt": { + "type": "string", + "description": "Latest deployment creation date in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "latestDeploymentStatus": { + "type": "string", + "description": "Status of latest deployment. Possible values are \"waiting\", \"processing\", \"building\", \"ready\", and \"failed\".", + "x-example": "ready" + }, "scopes": { "type": "array", "description": "Allowed permission scopes.", @@ -37275,6 +37330,10 @@ "logging", "runtime", "deploymentId", + "deploymentCreatedAt", + "latestDeploymentId", + "latestDeploymentCreatedAt", + "latestDeploymentStatus", "scopes", "vars", "events", @@ -37926,13 +37985,19 @@ "type": "string", "description": "Default output directory of build.", "x-example": ".\/dist" + }, + "fallbackFile": { + "type": "string", + "description": "Name of fallback file to use instead of 404 page. If null, Appwrite 404 page will be displayed.", + "x-example": "index.html" } }, "required": [ "key", "installCommand", "buildCommand", - "outputDirectory" + "outputDirectory", + "fallbackFile" ] }, "deployment": { @@ -37986,6 +38051,12 @@ "x-example": 128, "format": "int32" }, + "totalSize": { + "type": "integer", + "description": "The total size in bytes (source and build output).", + "x-example": 128, + "format": "int32" + }, "buildId": { "type": "string", "description": "The current build ID.", @@ -38008,7 +38079,7 @@ }, "status": { "type": "string", - "description": "The deployment status. Possible values are \"waiting\", \"processing\", \"building\", \"waiting\", \"ready\", and \"failed\".", + "description": "The deployment status. Possible values are \"waiting\", \"processing\", \"building\", \"ready\", and \"failed\".", "x-example": "ready" }, "buildLogs": { @@ -38083,6 +38154,7 @@ "entrypoint", "sourceSize", "buildSize", + "totalSize", "buildId", "activate", "screenshotLight", diff --git a/app/config/specs/open-api3-latest-server.json b/app/config/specs/open-api3-latest-server.json index c7704fd6a8..287ce791c3 100644 --- a/app/config/specs/open-api3-latest-server.json +++ b/app/config/specs/open-api3-latest-server.json @@ -9005,7 +9005,7 @@ }, { "name": "queries", - "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: buildSize, sourceSize, buildDuration, status, activate, type", + "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: buildSize, sourceSize, totalSize, buildDuration, status, activate, type", "required": false, "schema": { "type": "array", @@ -17574,7 +17574,7 @@ }, { "name": "queries", - "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: buildSize, sourceSize, buildDuration, status, activate, type", + "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: buildSize, sourceSize, totalSize, buildDuration, status, activate, type", "required": false, "schema": { "type": "array", @@ -27423,6 +27423,36 @@ "description": "Site's active deployment ID.", "x-example": "5e5ea5c16897e" }, + "deploymentCreatedAt": { + "type": "string", + "description": "Active deployment creation date in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "deploymentScreenshotLight": { + "type": "string", + "description": "Screenshot of active deployment with light theme preference file ID.", + "x-example": "5e5ea5c16897e" + }, + "deploymentScreenshotDark": { + "type": "string", + "description": "Screenshot of active deployment with dark theme preference file ID.", + "x-example": "5e5ea5c16897e" + }, + "latestDeploymentId": { + "type": "string", + "description": "Site's latest deployment ID.", + "x-example": "5e5ea5c16897e" + }, + "latestDeploymentCreatedAt": { + "type": "string", + "description": "Latest deployment creation date in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "latestDeploymentStatus": { + "type": "string", + "description": "Status of latest deployment. Possible values are \"waiting\", \"processing\", \"building\", \"ready\", and \"failed\".", + "x-example": "ready" + }, "vars": { "type": "array", "description": "Site variables.", @@ -27507,6 +27537,12 @@ "live", "framework", "deploymentId", + "deploymentCreatedAt", + "deploymentScreenshotLight", + "deploymentScreenshotDark", + "latestDeploymentId", + "latestDeploymentCreatedAt", + "latestDeploymentStatus", "vars", "timeout", "installCommand", @@ -27580,6 +27616,26 @@ "description": "Function's active deployment ID.", "x-example": "5e5ea5c16897e" }, + "deploymentCreatedAt": { + "type": "string", + "description": "Active deployment creation date in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "latestDeploymentId": { + "type": "string", + "description": "Function's latest deployment ID.", + "x-example": "5e5ea5c16897e" + }, + "latestDeploymentCreatedAt": { + "type": "string", + "description": "Latest deployment creation date in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "latestDeploymentStatus": { + "type": "string", + "description": "Status of latest deployment. Possible values are \"waiting\", \"processing\", \"building\", \"ready\", and \"failed\".", + "x-example": "ready" + }, "scopes": { "type": "array", "description": "Allowed permission scopes.", @@ -27672,6 +27728,10 @@ "logging", "runtime", "deploymentId", + "deploymentCreatedAt", + "latestDeploymentId", + "latestDeploymentCreatedAt", + "latestDeploymentStatus", "scopes", "vars", "events", @@ -27825,13 +27885,19 @@ "type": "string", "description": "Default output directory of build.", "x-example": ".\/dist" + }, + "fallbackFile": { + "type": "string", + "description": "Name of fallback file to use instead of 404 page. If null, Appwrite 404 page will be displayed.", + "x-example": "index.html" } }, "required": [ "key", "installCommand", "buildCommand", - "outputDirectory" + "outputDirectory", + "fallbackFile" ] }, "deployment": { @@ -27885,6 +27951,12 @@ "x-example": 128, "format": "int32" }, + "totalSize": { + "type": "integer", + "description": "The total size in bytes (source and build output).", + "x-example": 128, + "format": "int32" + }, "buildId": { "type": "string", "description": "The current build ID.", @@ -27907,7 +27979,7 @@ }, "status": { "type": "string", - "description": "The deployment status. Possible values are \"waiting\", \"processing\", \"building\", \"waiting\", \"ready\", and \"failed\".", + "description": "The deployment status. Possible values are \"waiting\", \"processing\", \"building\", \"ready\", and \"failed\".", "x-example": "ready" }, "buildLogs": { @@ -27982,6 +28054,7 @@ "entrypoint", "sourceSize", "buildSize", + "totalSize", "buildId", "activate", "screenshotLight", diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index 4bfad91074..05acf4a551 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -10322,7 +10322,7 @@ }, { "name": "queries", - "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: buildSize, sourceSize, buildDuration, status, activate, type", + "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: buildSize, sourceSize, totalSize, buildDuration, status, activate, type", "required": false, "type": "array", "collectionFormat": "multi", @@ -19823,8 +19823,7 @@ "default": "default", "x-example": "default", "enum": [ - "default", - "fra" + "default" ], "x-enum-name": null, "x-enum-keys": [] @@ -26230,7 +26229,7 @@ }, { "name": "queries", - "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: buildSize, sourceSize, buildDuration, status, activate, type", + "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: buildSize, sourceSize, totalSize, buildDuration, status, activate, type", "required": false, "type": "array", "collectionFormat": "multi", @@ -37455,6 +37454,36 @@ "description": "Site's active deployment ID.", "x-example": "5e5ea5c16897e" }, + "deploymentCreatedAt": { + "type": "string", + "description": "Active deployment creation date in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "deploymentScreenshotLight": { + "type": "string", + "description": "Screenshot of active deployment with light theme preference file ID.", + "x-example": "5e5ea5c16897e" + }, + "deploymentScreenshotDark": { + "type": "string", + "description": "Screenshot of active deployment with dark theme preference file ID.", + "x-example": "5e5ea5c16897e" + }, + "latestDeploymentId": { + "type": "string", + "description": "Site's latest deployment ID.", + "x-example": "5e5ea5c16897e" + }, + "latestDeploymentCreatedAt": { + "type": "string", + "description": "Latest deployment creation date in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "latestDeploymentStatus": { + "type": "string", + "description": "Status of latest deployment. Possible values are \"waiting\", \"processing\", \"building\", \"ready\", and \"failed\".", + "x-example": "ready" + }, "vars": { "type": "array", "description": "Site variables.", @@ -37540,6 +37569,12 @@ "live", "framework", "deploymentId", + "deploymentCreatedAt", + "deploymentScreenshotLight", + "deploymentScreenshotDark", + "latestDeploymentId", + "latestDeploymentCreatedAt", + "latestDeploymentStatus", "vars", "timeout", "installCommand", @@ -37766,6 +37801,26 @@ "description": "Function's active deployment ID.", "x-example": "5e5ea5c16897e" }, + "deploymentCreatedAt": { + "type": "string", + "description": "Active deployment creation date in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "latestDeploymentId": { + "type": "string", + "description": "Function's latest deployment ID.", + "x-example": "5e5ea5c16897e" + }, + "latestDeploymentCreatedAt": { + "type": "string", + "description": "Latest deployment creation date in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "latestDeploymentStatus": { + "type": "string", + "description": "Status of latest deployment. Possible values are \"waiting\", \"processing\", \"building\", \"ready\", and \"failed\".", + "x-example": "ready" + }, "scopes": { "type": "array", "description": "Allowed permission scopes.", @@ -37859,6 +37914,10 @@ "logging", "runtime", "deploymentId", + "deploymentCreatedAt", + "latestDeploymentId", + "latestDeploymentCreatedAt", + "latestDeploymentStatus", "scopes", "vars", "events", @@ -38513,13 +38572,19 @@ "type": "string", "description": "Default output directory of build.", "x-example": ".\/dist" + }, + "fallbackFile": { + "type": "string", + "description": "Name of fallback file to use instead of 404 page. If null, Appwrite 404 page will be displayed.", + "x-example": "index.html" } }, "required": [ "key", "installCommand", "buildCommand", - "outputDirectory" + "outputDirectory", + "fallbackFile" ] }, "deployment": { @@ -38573,6 +38638,12 @@ "x-example": 128, "format": "int32" }, + "totalSize": { + "type": "integer", + "description": "The total size in bytes (source and build output).", + "x-example": 128, + "format": "int32" + }, "buildId": { "type": "string", "description": "The current build ID.", @@ -38595,7 +38666,7 @@ }, "status": { "type": "string", - "description": "The deployment status. Possible values are \"waiting\", \"processing\", \"building\", \"waiting\", \"ready\", and \"failed\".", + "description": "The deployment status. Possible values are \"waiting\", \"processing\", \"building\", \"ready\", and \"failed\".", "x-example": "ready" }, "buildLogs": { @@ -38670,6 +38741,7 @@ "entrypoint", "sourceSize", "buildSize", + "totalSize", "buildId", "activate", "screenshotLight", diff --git a/app/config/specs/swagger2-latest-server.json b/app/config/specs/swagger2-latest-server.json index c86f605f23..b7371b1bbd 100644 --- a/app/config/specs/swagger2-latest-server.json +++ b/app/config/specs/swagger2-latest-server.json @@ -9188,7 +9188,7 @@ }, { "name": "queries", - "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: buildSize, sourceSize, buildDuration, status, activate, type", + "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: buildSize, sourceSize, totalSize, buildDuration, status, activate, type", "required": false, "type": "array", "collectionFormat": "multi", @@ -18075,7 +18075,7 @@ }, { "name": "queries", - "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: buildSize, sourceSize, buildDuration, status, activate, type", + "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: buildSize, sourceSize, totalSize, buildDuration, status, activate, type", "required": false, "type": "array", "collectionFormat": "multi", @@ -27974,6 +27974,36 @@ "description": "Site's active deployment ID.", "x-example": "5e5ea5c16897e" }, + "deploymentCreatedAt": { + "type": "string", + "description": "Active deployment creation date in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "deploymentScreenshotLight": { + "type": "string", + "description": "Screenshot of active deployment with light theme preference file ID.", + "x-example": "5e5ea5c16897e" + }, + "deploymentScreenshotDark": { + "type": "string", + "description": "Screenshot of active deployment with dark theme preference file ID.", + "x-example": "5e5ea5c16897e" + }, + "latestDeploymentId": { + "type": "string", + "description": "Site's latest deployment ID.", + "x-example": "5e5ea5c16897e" + }, + "latestDeploymentCreatedAt": { + "type": "string", + "description": "Latest deployment creation date in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "latestDeploymentStatus": { + "type": "string", + "description": "Status of latest deployment. Possible values are \"waiting\", \"processing\", \"building\", \"ready\", and \"failed\".", + "x-example": "ready" + }, "vars": { "type": "array", "description": "Site variables.", @@ -28059,6 +28089,12 @@ "live", "framework", "deploymentId", + "deploymentCreatedAt", + "deploymentScreenshotLight", + "deploymentScreenshotDark", + "latestDeploymentId", + "latestDeploymentCreatedAt", + "latestDeploymentStatus", "vars", "timeout", "installCommand", @@ -28132,6 +28168,26 @@ "description": "Function's active deployment ID.", "x-example": "5e5ea5c16897e" }, + "deploymentCreatedAt": { + "type": "string", + "description": "Active deployment creation date in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "latestDeploymentId": { + "type": "string", + "description": "Function's latest deployment ID.", + "x-example": "5e5ea5c16897e" + }, + "latestDeploymentCreatedAt": { + "type": "string", + "description": "Latest deployment creation date in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "latestDeploymentStatus": { + "type": "string", + "description": "Status of latest deployment. Possible values are \"waiting\", \"processing\", \"building\", \"ready\", and \"failed\".", + "x-example": "ready" + }, "scopes": { "type": "array", "description": "Allowed permission scopes.", @@ -28225,6 +28281,10 @@ "logging", "runtime", "deploymentId", + "deploymentCreatedAt", + "latestDeploymentId", + "latestDeploymentCreatedAt", + "latestDeploymentStatus", "scopes", "vars", "events", @@ -28379,13 +28439,19 @@ "type": "string", "description": "Default output directory of build.", "x-example": ".\/dist" + }, + "fallbackFile": { + "type": "string", + "description": "Name of fallback file to use instead of 404 page. If null, Appwrite 404 page will be displayed.", + "x-example": "index.html" } }, "required": [ "key", "installCommand", "buildCommand", - "outputDirectory" + "outputDirectory", + "fallbackFile" ] }, "deployment": { @@ -28439,6 +28505,12 @@ "x-example": 128, "format": "int32" }, + "totalSize": { + "type": "integer", + "description": "The total size in bytes (source and build output).", + "x-example": 128, + "format": "int32" + }, "buildId": { "type": "string", "description": "The current build ID.", @@ -28461,7 +28533,7 @@ }, "status": { "type": "string", - "description": "The deployment status. Possible values are \"waiting\", \"processing\", \"building\", \"waiting\", \"ready\", and \"failed\".", + "description": "The deployment status. Possible values are \"waiting\", \"processing\", \"building\", \"ready\", and \"failed\".", "x-example": "ready" }, "buildLogs": { @@ -28536,6 +28608,7 @@ "entrypoint", "sourceSize", "buildSize", + "totalSize", "buildId", "activate", "screenshotLight", diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index d8d496089c..0c37e1a765 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -3355,7 +3355,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') $processDocument($collection, $document); $queueForStatsUsage - ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, $operations) + ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, max($operations, 1)) ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), $operations) ->addMetric(str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getInternalId(), $collection->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_STORAGE), 1); // per collection @@ -3520,7 +3520,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') } $queueForStatsUsage - ->addMetric(METRIC_DATABASES_OPERATIONS_READS, $operations) + ->addMetric(METRIC_DATABASES_OPERATIONS_READS, max($operations, 1)) ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_READS), $operations); $response->addHeader('X-Debug-Operations', $operations); @@ -3661,7 +3661,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen $processDocument($collection, $document); $queueForStatsUsage - ->addMetric(METRIC_DATABASES_OPERATIONS_READS, $operations) + ->addMetric(METRIC_DATABASES_OPERATIONS_READS, max($operations, 1)) ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_READS), $operations); $response->addHeader('X-Debug-Operations', $operations); @@ -3959,7 +3959,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum $setCollection($collection, $newDocument); $queueForStatsUsage - ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, $operations) + ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, max($operations, 1)) ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), $operations); $response->addHeader('X-Debug-Operations', $operations); diff --git a/app/controllers/api/messaging.php b/app/controllers/api/messaging.php index 9393f1fbfe..178266db60 100644 --- a/app/controllers/api/messaging.php +++ b/app/controllers/api/messaging.php @@ -2936,7 +2936,7 @@ App::post('/v1/messaging/messages/email') break; case MessageStatus::SCHEDULED: $schedule = $dbForPlatform->createDocument('schedules', new Document([ - 'region' => System::getEnv('_APP_REGION', 'default'), + 'region' => $project->getAttribute('region'), 'resourceType' => 'message', 'resourceId' => $message->getId(), 'resourceInternalId' => $message->getInternalId(), @@ -3058,7 +3058,7 @@ App::post('/v1/messaging/messages/sms') break; case MessageStatus::SCHEDULED: $schedule = $dbForPlatform->createDocument('schedules', new Document([ - 'region' => System::getEnv('_APP_REGION', 'default'), + 'region' => $project->getAttribute('region'), 'resourceType' => 'message', 'resourceId' => $message->getId(), 'resourceInternalId' => $message->getInternalId(), @@ -3275,7 +3275,7 @@ App::post('/v1/messaging/messages/push') break; case MessageStatus::SCHEDULED: $schedule = $dbForPlatform->createDocument('schedules', new Document([ - 'region' => System::getEnv('_APP_REGION', 'default'), + 'region' => $project->getAttribute('region'), 'resourceType' => 'message', 'resourceId' => $message->getId(), 'resourceInternalId' => $message->getInternalId(), @@ -3661,7 +3661,7 @@ App::patch('/v1/messaging/messages/email/:messageId') if (\is_null($currentScheduledAt) && !\is_null($scheduledAt)) { $schedule = $dbForPlatform->createDocument('schedules', new Document([ - 'region' => System::getEnv('_APP_REGION', 'default'), + 'region' => $project->getAttribute('region'), 'resourceType' => 'message', 'resourceId' => $message->getId(), 'resourceInternalId' => $message->getInternalId(), @@ -3862,7 +3862,7 @@ App::patch('/v1/messaging/messages/sms/:messageId') if (\is_null($currentScheduledAt) && !\is_null($scheduledAt)) { $schedule = $dbForPlatform->createDocument('schedules', new Document([ - 'region' => System::getEnv('_APP_REGION', 'default'), + 'region' => $project->getAttribute('region'), 'resourceType' => 'message', 'resourceId' => $message->getId(), 'resourceInternalId' => $message->getInternalId(), @@ -4035,7 +4035,7 @@ App::patch('/v1/messaging/messages/push/:messageId') if (\is_null($currentScheduledAt) && !\is_null($scheduledAt)) { $schedule = $dbForPlatform->createDocument('schedules', new Document([ - 'region' => System::getEnv('_APP_REGION', 'default'), + 'region' => $project->getAttribute('region'), 'resourceType' => 'message', 'resourceId' => $message->getId(), 'resourceInternalId' => $message->getInternalId(), diff --git a/app/controllers/api/vcs.php b/app/controllers/api/vcs.php index 89a0e251d4..107be3d888 100644 --- a/app/controllers/api/vcs.php +++ b/app/controllers/api/vcs.php @@ -253,6 +253,13 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId 'activate' => $activate, ])); + $resource = $resource + ->setAttribute('latestDeploymentId', $deployment->getId()) + ->setAttribute('latestDeploymentInternalId', $deployment->getInternalId()) + ->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt()) + ->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); + $dbForProject->updateDocument($resource->getCollection(), $resource->getId(), $resource); + if ($resource->getCollection() === 'sites') { $projectId = $project->getId(); diff --git a/app/init/constants.php b/app/init/constants.php index d46d3ed79c..e5e7b5900d 100644 --- a/app/init/constants.php +++ b/app/init/constants.php @@ -238,6 +238,7 @@ const METRIC_TOPICS = 'topics'; const METRIC_TARGETS = 'targets'; const METRIC_PROVIDER_TYPE_TARGETS = '{providerType}.targets'; const METRIC_KEYS = 'keys'; +const METRIC_DOMAINS = 'domains'; const METRIC_RESOURCE_TYPE_ID_BUILDS = '{resourceType}.{resourceInternalId}.builds'; const METRIC_RESOURCE_TYPE_ID_BUILDS_STORAGE = '{resourceType}.{resourceInternalId}.builds.storage'; const METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS = '{resourceType}.{resourceInternalId}.deployments'; diff --git a/app/init/registers.php b/app/init/registers.php index 9c90eb54d2..1ebcbc1691 100644 --- a/app/init/registers.php +++ b/app/init/registers.php @@ -217,7 +217,7 @@ $register->set('pools', function () { return new PDOProxy(function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) { return new PDO("mysql:host={$dsnHost};port={$dsnPort};dbname={$dsnDatabase};charset=utf8mb4", $dsnUser, $dsnPass, array( PDO::ATTR_TIMEOUT => 3, // Seconds - PDO::ATTR_PERSISTENT => true, + PDO::ATTR_PERSISTENT => false, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => true, PDO::ATTR_STRINGIFY_FETCHES => true diff --git a/app/views/install/compose.phtml b/app/views/install/compose.phtml index 12c450bf04..523fee911a 100644 --- a/app/views/install/compose.phtml +++ b/app/views/install/compose.phtml @@ -165,7 +165,6 @@ $image = $this->getParam('image', ''); - _APP_MIGRATIONS_FIREBASE_CLIENT_ID - _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET - _APP_ASSISTANT_OPENAI_API_KEY - appwrite-console: <<: *x-logging container_name: appwrite-console diff --git a/composer.json b/composer.json index c25efaca10..66b18c7a7b 100644 --- a/composer.json +++ b/composer.json @@ -62,10 +62,10 @@ "utopia-php/locale": "0.4.*", "utopia-php/logger": "0.6.*", "utopia-php/messaging": "0.16.*", - "utopia-php/migration": "0.6.*", + "utopia-php/migration": "0.8.*", "utopia-php/orchestration": "0.9.*", "utopia-php/platform": "0.7.*", - "utopia-php/pools": "0.5.*", + "utopia-php/pools": "0.7.*", "utopia-php/preloader": "0.2.*", "utopia-php/queue": "0.9.*", "utopia-php/registry": "0.5.*", diff --git a/composer.lock b/composer.lock index 3ac8abaead..aa9598eb05 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "eca74b44aa6dc476aa8056344e87f363", + "content-hash": "eeb306ee6d2576e466ceea5e927664d0", "packages": [ { "name": "adhocore/jwt", @@ -753,16 +753,16 @@ }, { "name": "jean85/pretty-package-versions", - "version": "2.1.0", + "version": "2.1.1", "source": { "type": "git", "url": "https://github.com/Jean85/pretty-package-versions.git", - "reference": "3c4e5f62ba8d7de1734312e4fff32f67a8daaf10" + "reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/3c4e5f62ba8d7de1734312e4fff32f67a8daaf10", - "reference": "3c4e5f62ba8d7de1734312e4fff32f67a8daaf10", + "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/4d7aa5dab42e2a76d99559706022885de0e18e1a", + "reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a", "shasum": "" }, "require": { @@ -772,8 +772,9 @@ "require-dev": { "friendsofphp/php-cs-fixer": "^3.2", "jean85/composer-provided-replaced-stub-package": "^1.0", - "phpstan/phpstan": "^1.4", + "phpstan/phpstan": "^2.0", "phpunit/phpunit": "^7.5|^8.5|^9.6", + "rector/rector": "^2.0", "vimeo/psalm": "^4.3 || ^5.0" }, "type": "library", @@ -806,9 +807,9 @@ ], "support": { "issues": "https://github.com/Jean85/pretty-package-versions/issues", - "source": "https://github.com/Jean85/pretty-package-versions/tree/2.1.0" + "source": "https://github.com/Jean85/pretty-package-versions/tree/2.1.1" }, - "time": "2024-11-18T16:19:46+00:00" + "time": "2025-03-19T14:43:43+00:00" }, { "name": "league/csv", @@ -4204,16 +4205,16 @@ }, { "name": "utopia-php/migration", - "version": "0.6.22", + "version": "0.8.1", "source": { "type": "git", "url": "https://github.com/utopia-php/migration.git", - "reference": "a0269746bd318ff0993f5aa008675b971689d5b5" + "reference": "36ec7af2e6bf78de5d86e1b0a953fd7dcdf69dab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/a0269746bd318ff0993f5aa008675b971689d5b5", - "reference": "a0269746bd318ff0993f5aa008675b971689d5b5", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/36ec7af2e6bf78de5d86e1b0a953fd7dcdf69dab", + "reference": "36ec7af2e6bf78de5d86e1b0a953fd7dcdf69dab", "shasum": "" }, "require": { @@ -4254,9 +4255,9 @@ ], "support": { "issues": "https://github.com/utopia-php/migration/issues", - "source": "https://github.com/utopia-php/migration/tree/0.6.22" + "source": "https://github.com/utopia-php/migration/tree/0.8.1" }, - "time": "2025-03-13T07:35:55+00:00" + "time": "2025-03-18T07:48:08+00:00" }, { "name": "utopia-php/mongo", @@ -4420,25 +4421,26 @@ }, { "name": "utopia-php/pools", - "version": "0.5.0", + "version": "0.7.0", "source": { "type": "git", "url": "https://github.com/utopia-php/pools.git", - "reference": "6f716a213a08db95eda1b5dddfa90983c1834817" + "reference": "ad64d45afda08ec8b29e2642a8d18075964d40bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/pools/zipball/6f716a213a08db95eda1b5dddfa90983c1834817", - "reference": "6f716a213a08db95eda1b5dddfa90983c1834817", + "url": "https://api.github.com/repos/utopia-php/pools/zipball/ad64d45afda08ec8b29e2642a8d18075964d40bf", + "reference": "ad64d45afda08ec8b29e2642a8d18075964d40bf", "shasum": "" }, "require": { - "php": ">=8.0" + "php": ">=8.3", + "utopia-php/telemetry": "0.1.*" }, "require-dev": { - "laravel/pint": "1.2.*", - "phpstan/phpstan": "1.8.*", - "phpunit/phpunit": "^9.3" + "laravel/pint": "1.*", + "phpstan/phpstan": "1.*", + "phpunit/phpunit": "11.*" }, "type": "library", "autoload": { @@ -4465,9 +4467,9 @@ ], "support": { "issues": "https://github.com/utopia-php/pools/issues", - "source": "https://github.com/utopia-php/pools/tree/0.5.0" + "source": "https://github.com/utopia-php/pools/tree/0.7.0" }, - "time": "2024-04-19T11:11:54+00:00" + "time": "2025-03-18T03:55:33+00:00" }, { "name": "utopia-php/preloader", @@ -8505,7 +8507,7 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": {}, + "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { @@ -8529,5 +8531,5 @@ "platform-overrides": { "php": "8.3" }, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.3.0" } diff --git a/docker-compose.yml b/docker-compose.yml index 4d6dfaf682..64aee1c2b4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -764,7 +764,7 @@ services: - _APP_LOGGING_CONFIG - _APP_DATABASE_SHARED_TABLES - _APP_STATS_RESOURCES_INTERVAL - + appwrite-worker-stats-resources: entrypoint: worker-stats-resources <<: *x-logging @@ -795,7 +795,7 @@ services: - _APP_LOGGING_CONFIG - _APP_USAGE_AGGREGATION_INTERVAL - _APP_DATABASE_SHARED_TABLES - + appwrite-worker-stats-usage: entrypoint: worker-stats-usage <<: *x-logging @@ -858,7 +858,7 @@ services: - _APP_USAGE_AGGREGATION_INTERVAL - _APP_DATABASE_SHARED_TABLES - _APP_STATS_USAGE_DUAL_WRITING_DBS - + appwrite-task-scheduler-functions: entrypoint: schedule-functions <<: *x-logging diff --git a/src/Appwrite/Platform/Modules/Compute/Base.php b/src/Appwrite/Platform/Modules/Compute/Base.php index 1e70c106a7..6d44be239d 100644 --- a/src/Appwrite/Platform/Modules/Compute/Base.php +++ b/src/Appwrite/Platform/Modules/Compute/Base.php @@ -94,6 +94,13 @@ class Base extends Action 'activate' => $activate, ])); + $function = $function + ->setAttribute('latestDeploymentId', $deployment->getId()) + ->setAttribute('latestDeploymentInternalId', $deployment->getInternalId()) + ->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt()) + ->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); + $dbForProject->updateDocument('functions', $function->getId(), $function); + $queueForBuilds ->setType(BUILD_TYPE_DEPLOYMENT) ->setResource($function) @@ -185,6 +192,13 @@ class Base extends Action 'activate' => $activate, ])); + $site = $site + ->setAttribute('latestDeploymentId', $deployment->getId()) + ->setAttribute('latestDeploymentInternalId', $deployment->getInternalId()) + ->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt()) + ->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); + $dbForProject->updateDocument('sites', $site->getId(), $site); + $sitesDomain = System::getEnv('_APP_DOMAIN_SITES', ''); $domain = ID::unique() . "." . $sitesDomain; diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Create.php index 1f49568249..00c4f402a1 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Create.php @@ -240,6 +240,13 @@ class Create extends Action 'sourceMetadata' => $metadata, 'type' => $type ])); + + $function = $function + ->setAttribute('latestDeploymentId', $deployment->getId()) + ->setAttribute('latestDeploymentInternalId', $deployment->getInternalId()) + ->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt()) + ->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); + $dbForProject->updateDocument('functions', $function->getId(), $function); } else { $deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment->setAttribute('sourceSize', $fileSize)->setAttribute('sourceMetadata', $metadata)); } @@ -273,6 +280,13 @@ class Create extends Action 'sourceMetadata' => $metadata, 'type' => $type ])); + + $function = $function + ->setAttribute('latestDeploymentId', $deployment->getId()) + ->setAttribute('latestDeploymentInternalId', $deployment->getInternalId()) + ->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt()) + ->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); + $dbForProject->updateDocument('functions', $function->getId(), $function); } else { $deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment->setAttribute('sourceChunksUploaded', $chunksUploaded)->setAttribute('sourceMetadata', $metadata)); } diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Delete.php b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Delete.php index 27eded178a..b66ea30bb2 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Delete.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Delete.php @@ -12,6 +12,7 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response; use Utopia\Database\Database; use Utopia\Database\Document; +use Utopia\Database\Query; use Utopia\Database\Validator\UID; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; @@ -96,6 +97,23 @@ class Delete extends Action } } + if ($function->getAttribute('latestDeploymentId') === $deployment->getId()) { + $latestDeployment = $dbForProject->findOne('deployments', [ + Query::equal('resourceType', ['functions']), + Query::equal('resourceInternalId', [$function->getInternalId()]), + Query::orderDesc('$createdAt'), + ]); + $function = $dbForProject->updateDocument( + 'functions', + $function->getId(), + $function + ->setAttribute('latestDeploymentCreatedAt', $latestDeployment->isEmpty() ? '' : $latestDeployment->getCreatedAt()) + ->setAttribute('latestDeploymentInternalId', $latestDeployment->isEmpty() ? '' : $latestDeployment->getInternalId()) + ->setAttribute('latestDeploymentId', $latestDeployment->isEmpty() ? '' : $latestDeployment->getId()) + ->setAttribute('latestDeploymentStatus', $latestDeployment->isEmpty() ? '' : $latestDeployment->getAttribute('status', '')) + ); + } + if ($function->getAttribute('deploymentId') === $deployment->getId()) { // Reset function deployment $function = $dbForProject->updateDocument('functions', $function->getId(), new Document(array_merge($function->getArrayCopy(), [ 'deploymentId' => '', diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Duplicate/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Duplicate/Create.php index aa1142570b..eb672dacf9 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Duplicate/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Duplicate/Create.php @@ -113,6 +113,13 @@ class Create extends Action 'buildLogs' => '', ])); + $function = $function + ->setAttribute('latestDeploymentId', $deployment->getId()) + ->setAttribute('latestDeploymentInternalId', $deployment->getInternalId()) + ->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt()) + ->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); + $dbForProject->updateDocument('functions', $function->getId(), $function); + $queueForBuilds ->setType(BUILD_TYPE_DEPLOYMENT) ->setResource($function) diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Status/Update.php b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Status/Update.php index 6d5cc145c9..053a10f982 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Status/Update.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Status/Update.php @@ -95,6 +95,11 @@ class Update extends Action 'status' => 'canceled' ])); + if ($deployment->getInternalId() === $function->getAttribute('latestDeploymentInternalId', '')) { + $function = $function->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); + $dbForProject->updateDocument('functions', $function->getId(), $function); + } + try { $executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST')); $executor->deleteRuntime($project->getId(), $deploymentId . "-build"); diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Template/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Template/Create.php index 9d48c1c112..98a975545d 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Template/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Template/Create.php @@ -150,6 +150,13 @@ class Create extends Base 'activate' => $activate, ])); + $function = $function + ->setAttribute('latestDeploymentId', $deployment->getId()) + ->setAttribute('latestDeploymentInternalId', $deployment->getInternalId()) + ->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt()) + ->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); + $dbForProject->updateDocument('functions', $function->getId(), $function); + $queueForBuilds ->setType(BUILD_TYPE_DEPLOYMENT) ->setResource($function) diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php index 2e47c04276..7e89196a5b 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php @@ -300,7 +300,7 @@ class Create extends Base ]; $schedule = $dbForPlatform->createDocument('schedules', new Document([ - 'region' => System::getEnv('_APP_REGION', 'default'), + 'region' => $project->getAttribute('region'), 'resourceType' => ScheduleExecutions::getSupportedResource(), 'resourceId' => $execution->getId(), 'resourceInternalId' => $execution->getInternalId(), diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php index 552d9655f1..28f2c3cd1f 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php @@ -207,7 +207,7 @@ class Create extends Base $schedule = Authorization::skip( fn () => $dbForPlatform->createDocument('schedules', new Document([ - 'region' => System::getEnv('_APP_REGION', 'default'), // Todo replace with projects region + 'region' => $project->getAttribute('region'), 'resourceType' => 'function', 'resourceId' => $function->getId(), 'resourceInternalId' => $function->getInternalId(), diff --git a/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php b/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php index 16b2e08be1..9ea5638ac3 100644 --- a/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php +++ b/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php @@ -91,8 +91,23 @@ class Builds extends Action * @return void * @throws \Utopia\Database\Exception */ - public function action(Message $message, Document $project, Database $dbForPlatform, Event $queueForEvents, Webhook $queueForWebhooks, Func $queueForFunctions, Realtime $queueForRealtime, StatsUsage $queueForStatsUsage, Cache $cache, Database $dbForProject, Device $deviceForFunctions, Device $deviceForSites, callable $isResourceBlocked, Device $deviceForFiles, Log $log): void - { + public function action( + Message $message, + Document $project, + Database $dbForPlatform, + Event $queueForEvents, + Webhook $queueForWebhooks, + Func $queueForFunctions, + Realtime $queueForRealtime, + StatsUsage $queueForStatsUsage, + Cache $cache, + Database $dbForProject, + Device $deviceForFunctions, + Device $deviceForSites, + callable $isResourceBlocked, + Device $deviceForFiles, + Log $log + ): void { $payload = $message->getPayload() ?? []; if (empty($payload)) { @@ -112,7 +127,25 @@ class Builds extends Action case BUILD_TYPE_RETRY: Console::info('Creating build for deployment: ' . $deployment->getId()); $github = new GitHub($cache); - $this->buildDeployment($deviceForFunctions, $deviceForSites, $deviceForFiles, $queueForWebhooks, $queueForFunctions, $queueForRealtime, $queueForEvents, $queueForStatsUsage, $dbForPlatform, $dbForProject, $github, $project, $resource, $deployment, $template, $isResourceBlocked, $log); + $this->buildDeployment( + $deviceForFunctions, + $deviceForSites, + $deviceForFiles, + $queueForWebhooks, + $queueForFunctions, + $queueForRealtime, + $queueForEvents, + $queueForStatsUsage, + $dbForPlatform, + $dbForProject, + $github, + $project, + $resource, + $deployment, + $template, + $isResourceBlocked, + $log + ); break; default: @@ -142,9 +175,26 @@ class Builds extends Action * * @throws Exception */ - protected function buildDeployment(Device $deviceForFunctions, Device $deviceForSites, Device $deviceForFiles, Webhook $queueForWebhooks, Func $queueForFunctions, Realtime $queueForRealtime, Event $queueForEvents, StatsUsage $queueForStatsUsage, Database $dbForPlatform, Database $dbForProject, GitHub $github, Document $project, Document $resource, Document $deployment, Document $template, callable $isResourceBlocked, Log $log): void - { - $resourceKey = match($resource->getCollection()) { + protected function buildDeployment( + Device $deviceForFunctions, + Device $deviceForSites, + Device $deviceForFiles, + Webhook $queueForWebhooks, + Func $queueForFunctions, + Realtime $queueForRealtime, + Event $queueForEvents, + StatsUsage $queueForStatsUsage, + Database $dbForPlatform, + Database $dbForProject, + GitHub $github, + Document $project, + Document $resource, + Document $deployment, + Document $template, + callable $isResourceBlocked, + Log $log + ): void { + $resourceKey = match ($resource->getCollection()) { 'functions' => 'functionId', 'sites' => 'siteId', default => throw new \Exception('Invalid resource type') @@ -211,6 +261,11 @@ class Builds extends Action $deployment->setAttribute('status', 'processing'); $deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment); + if ($deployment->getInternalId() === $resource->getAttribute('latestDeploymentInternalId', '')) { + $resource = $resource->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); + $dbForProject->updateDocument($resource->getCollection(), $resource->getId(), $resource); + } + $queueForRealtime ->setPayload($deployment->getArrayCopy()) ->trigger(); @@ -452,6 +507,11 @@ class Builds extends Action $deployment->setAttribute('status', 'building'); $deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment); + if ($deployment->getInternalId() === $resource->getAttribute('latestDeploymentInternalId', '')) { + $resource = $resource->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); + $dbForProject->updateDocument($resource->getCollection(), $resource->getId(), $resource); + } + $queueForRealtime ->setPayload($deployment->getArrayCopy()) ->trigger(); @@ -732,6 +792,11 @@ class Builds extends Action $deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment); + if ($deployment->getInternalId() === $resource->getAttribute('latestDeploymentInternalId', '')) { + $resource = $resource->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); + $dbForProject->updateDocument($resource->getCollection(), $resource->getId(), $resource); + } + $queueForRealtime ->setPayload($deployment->getArrayCopy()) ->trigger(); @@ -1017,6 +1082,11 @@ class Builds extends Action $deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment); + if ($deployment->getInternalId() === $resource->getAttribute('latestDeploymentInternalId', '')) { + $resource = $resource->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); + $dbForProject->updateDocument($resource->getCollection(), $resource->getId(), $resource); + } + $queueForRealtime ->setPayload($deployment->getArrayCopy()) ->trigger(); @@ -1170,8 +1240,18 @@ class Builds extends Action * @throws Conflict * @throws Restricted */ - protected function runGitAction(string $status, GitHub $github, string $providerCommitHash, string $owner, string $repositoryName, Document $project, Document $resource, string $deploymentId, Database $dbForProject, Database $dbForPlatform): void - { + protected function runGitAction( + string $status, + GitHub $github, + string $providerCommitHash, + string $owner, + string $repositoryName, + Document $project, + Document $resource, + string $deploymentId, + Database $dbForProject, + Database $dbForPlatform + ): void { if ($resource->getAttribute('providerSilentMode', false) === true) { return; } @@ -1201,7 +1281,14 @@ class Builds extends Action $protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https'; $hostname = System::getEnv('_APP_DOMAIN'); - $providerTargetUrl = "{$protocol}://{$hostname}/console/project-{$project->getId()}/functions/function-{$resource->getId()}"; + + $projectId = $project->getId(); + $resourceId = $resource->getId(); + $providerTargetUrl = match ($resource->getCollection()) { + 'functions' => "{$protocol}://{$hostname}/console/project-{$projectId}/functions/function-{$resourceId}", + 'sites' => "{$protocol}://{$hostname}/console/project-{$projectId}/sites/site-{$resourceId}", + default => throw new \Exception('Invalid resource type') + }; $github->updateCommitStatus($repositoryName, $providerCommitHash, $owner, $state, $message, $providerTargetUrl, $name); } diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php index c52f958da5..ab2d9282ac 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php @@ -250,6 +250,13 @@ class Create extends Action 'type' => $type ])); + $site = $site + ->setAttribute('latestDeploymentId', $deployment->getId()) + ->setAttribute('latestDeploymentInternalId', $deployment->getInternalId()) + ->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt()) + ->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); + $dbForProject->updateDocument('sites', $site->getId(), $site); + $sitesDomain = System::getEnv('_APP_DOMAIN_SITES', ''); $domain = ID::unique() . "." . $sitesDomain; @@ -310,6 +317,13 @@ class Create extends Action 'type' => $type ])); + $site = $site + ->setAttribute('latestDeploymentId', $deployment->getId()) + ->setAttribute('latestDeploymentInternalId', $deployment->getInternalId()) + ->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt()) + ->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); + $dbForProject->updateDocument('sites', $site->getId(), $site); + $sitesDomain = System::getEnv('_APP_DOMAIN_SITES', ''); $domain = ID::unique() . "." . $sitesDomain; $ruleId = md5($domain); diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Delete.php b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Delete.php index 00342a7667..9d30b21a11 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Delete.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Delete.php @@ -12,6 +12,7 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response; use Utopia\Database\Database; use Utopia\Database\Document; +use Utopia\Database\Query; use Utopia\Database\Validator\UID; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; @@ -96,6 +97,23 @@ class Delete extends Action } } + if ($site->getAttribute('latestDeploymentId') === $deployment->getId()) { + $latestDeployment = $dbForProject->findOne('deployments', [ + Query::equal('resourceType', ['sites']), + Query::equal('resourceInternalId', [$site->getInternalId()]), + Query::orderDesc('$createdAt'), + ]); + $site = $dbForProject->updateDocument( + 'sites', + $site->getId(), + $site + ->setAttribute('latestDeploymentCreatedAt', $latestDeployment->isEmpty() ? '' : $latestDeployment->getCreatedAt()) + ->setAttribute('latestDeploymentInternalId', $latestDeployment->isEmpty() ? '' : $latestDeployment->getInternalId()) + ->setAttribute('latestDeploymentId', $latestDeployment->isEmpty() ? '' : $latestDeployment->getId()) + ->setAttribute('latestDeploymentStatus', $latestDeployment->isEmpty() ? '' : $latestDeployment->getAttribute('status', '')) + ); + } + if ($site->getAttribute('deploymentId') === $deployment->getId()) { // Reset site deployment $site = $dbForProject->updateDocument('sites', $site->getId(), new Document(array_merge($site->getArrayCopy(), [ 'deploymentId' => '', diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Duplicate/Create.php b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Duplicate/Create.php index 9982828ec2..9e33a6b839 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Duplicate/Create.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Duplicate/Create.php @@ -126,6 +126,13 @@ class Create extends Action 'buildLogs' => '', ])); + $site = $site + ->setAttribute('latestDeploymentId', $deployment->getId()) + ->setAttribute('latestDeploymentInternalId', $deployment->getInternalId()) + ->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt()) + ->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); + $dbForProject->updateDocument('sites', $site->getId(), $site); + // Preview deployments for sites $sitesDomain = System::getEnv('_APP_DOMAIN_SITES', ''); $domain = ID::unique() . "." . $sitesDomain; diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Status/Update.php b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Status/Update.php index 0361c1bc9c..1970fa898e 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Status/Update.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Status/Update.php @@ -93,6 +93,11 @@ class Update extends Action 'status' => 'canceled' ])); + if ($deployment->getInternalId() === $site->getAttribute('latestDeploymentInternalId', '')) { + $site = $site->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); + $dbForProject->updateDocument('sites', $site->getId(), $site); + } + try { $executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST')); $executor->deleteRuntime($project->getId(), $deploymentId . "-build"); diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Template/Create.php b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Template/Create.php index 6e077f2713..c3a4b3ca96 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Template/Create.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Template/Create.php @@ -161,6 +161,13 @@ class Create extends Base 'activate' => $activate, ])); + $site = $site + ->setAttribute('latestDeploymentId', $deployment->getId()) + ->setAttribute('latestDeploymentInternalId', $deployment->getInternalId()) + ->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt()) + ->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); + $dbForProject->updateDocument('sites', $site->getId(), $site); + $sitesDomain = System::getEnv('_APP_DOMAIN_SITES', ''); $domain = ID::unique() . "." . $sitesDomain; diff --git a/src/Appwrite/Platform/Tasks/Maintenance.php b/src/Appwrite/Platform/Tasks/Maintenance.php index 1534dde397..198b701044 100644 --- a/src/Appwrite/Platform/Tasks/Maintenance.php +++ b/src/Appwrite/Platform/Tasks/Maintenance.php @@ -47,13 +47,15 @@ class Maintenance extends Action Console::info("[{$time}] Notifying workers with maintenance tasks every {$interval} seconds"); - $this->foreachProject($dbForPlatform, function (Document $project) use ($queueForDeletes, $usageStatsRetentionHourly) { + $dbForPlatform->foreach('projects', function (Document $project) use ($queueForDeletes, $usageStatsRetentionHourly) { $queueForDeletes ->setType(DELETE_TYPE_MAINTENANCE) ->setProject($project) ->setUsageRetentionHourlyDateTime(DateTime::addSeconds(new \DateTime(), -1 * $usageStatsRetentionHourly)) ->trigger(); - }); + }, [ + Query::limit(100), + ]); $queueForDeletes ->setType(DELETE_TYPE_MAINTENANCE) @@ -68,33 +70,6 @@ class Maintenance extends Action }, $interval, $delay); } - protected function foreachProject(Database $dbForPlatform, callable $callback): void - { - // TODO: @Meldiron name of this method no longer matches. It does not delete, and it gives whole document - $count = 0; - $chunk = 0; - $limit = 50; - $sum = $limit; - $executionStart = \microtime(true); - - while ($sum === $limit) { - $projects = $dbForPlatform->find('projects', [Query::limit($limit), Query::offset($chunk * $limit)]); - - $chunk++; - - /** @var string[] $projectIds */ - $sum = count($projects); - - foreach ($projects as $project) { - $callback($project); - $count++; - } - } - - $executionEnd = \microtime(true); - Console::info("Found {$count} projects " . ($executionEnd - $executionStart) . " seconds"); - } - private function notifyDeleteConnections(Delete $queueForDeletes): void { $queueForDeletes diff --git a/src/Appwrite/Platform/Workers/Certificates.php b/src/Appwrite/Platform/Workers/Certificates.php index 552f617656..712196fc72 100644 --- a/src/Appwrite/Platform/Workers/Certificates.php +++ b/src/Appwrite/Platform/Workers/Certificates.php @@ -69,8 +69,17 @@ class Certificates extends Action * @throws Throwable * @throws \Utopia\Database\Exception */ - public function action(Message $message, Database $dbForPlatform, Mail $queueForMails, Event $queueForEvents, Webhook $queueForWebhooks, Func $queueForFunctions, Realtime $queueForRealtime, Log $log, CertificatesAdapter $certificates): void - { + public function action( + Message $message, + Database $dbForPlatform, + Mail $queueForMails, + Event $queueForEvents, + Webhook $queueForWebhooks, + Func $queueForFunctions, + Realtime $queueForRealtime, + Log $log, + CertificatesAdapter $certificates + ): void { $payload = $message->getPayload() ?? []; if (empty($payload)) { @@ -99,8 +108,18 @@ class Certificates extends Action * @throws Throwable * @throws \Utopia\Database\Exception */ - private function execute(Domain $domain, Database $dbForPlatform, Mail $queueForMails, Event $queueForEvents, Webhook $queueForWebhooks, Func $queueForFunctions, Realtime $queueForRealtime, Log $log, CertificatesAdapter $certificates, bool $skipRenewCheck = false): void - { + private function execute( + Domain $domain, + Database $dbForPlatform, + Mail $queueForMails, + Event $queueForEvents, + Webhook $queueForWebhooks, + Func $queueForFunctions, + Realtime $queueForRealtime, + Log $log, + CertificatesAdapter $certificates, + bool $skipRenewCheck = false + ): void { /** * 1. Read arguments and validate domain * 2. Get main domain @@ -209,8 +228,16 @@ class Certificates extends Action * @throws Conflict * @throws Structure */ - private function saveCertificateDocument(string $domain, Document $certificate, bool $success, Database $dbForPlatform, Event $queueForEvents, Webhook $queueForWebhooks, Func $queueForFunctions, Realtime $queueForRealtime): void - { + private function saveCertificateDocument( + string $domain, + Document $certificate, + bool $success, + Database $dbForPlatform, + Event $queueForEvents, + Webhook $queueForWebhooks, + Func $queueForFunctions, + Realtime $queueForRealtime + ): void { // Check if update or insert required $certificateDocument = $dbForPlatform->findOne('certificates', [Query::equal('domain', [$domain])]); if (!$certificateDocument->isEmpty()) { @@ -342,8 +369,16 @@ class Certificates extends Action * * @return void */ - private function updateDomainDocuments(string $certificateId, string $domain, bool $success, Database $dbForPlatform, Event $queueForEvents, Webhook $queueForWebhooks, Func $queueForFunctions, Realtime $queueForRealtime): void - { + private function updateDomainDocuments( + string $certificateId, + string $domain, + bool $success, + Database $dbForPlatform, + Event $queueForEvents, + Webhook $queueForWebhooks, + Func $queueForFunctions, + Realtime $queueForRealtime + ): void { // TODO: @christyjacob remove once we migrate the rules in 1.7.x if (System::getEnv('_APP_RULES_FORMAT') === 'md5') { $rule = $dbForPlatform->getDocument('rules', md5($domain)); diff --git a/src/Appwrite/Platform/Workers/Databases.php b/src/Appwrite/Platform/Workers/Databases.php index e8edb1c0a3..010973519c 100644 --- a/src/Appwrite/Platform/Workers/Databases.php +++ b/src/Appwrite/Platform/Workers/Databases.php @@ -98,8 +98,15 @@ class Databases extends Action * @throws \Exception * @throws \Throwable */ - private function createAttribute(Document $database, Document $collection, Document $attribute, Document $project, Database $dbForPlatform, Database $dbForProject, Realtime $queueForRealtime): void - { + private function createAttribute( + Document $database, + Document $collection, + Document $attribute, + Document $project, + Database $dbForPlatform, + Database $dbForProject, + Realtime $queueForRealtime + ): void { if ($collection->isEmpty()) { throw new Exception('Missing collection'); } diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index 7be3cba7ad..4b5b11bce7 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -65,8 +65,22 @@ class Deletes extends Action * @throws Exception * @throws Throwable */ - public function action(Message $message, Document $project, Database $dbForPlatform, callable $getProjectDB, callable $getLogsDB, Device $deviceForFiles, Device $deviceForFunctions, Device $deviceForSites, Device $deviceForBuilds, Device $deviceForCache, CertificatesAdapter $certificates, string $executionRetention, string $auditRetention, Log $log): void - { + public function action( + Message $message, + Document $project, + Database $dbForPlatform, + callable $getProjectDB, + callable $getLogsDB, + Device $deviceForFiles, + Device $deviceForFunctions, + Device $deviceForSites, + Device $deviceForBuilds, + Device $deviceForCache, + CertificatesAdapter $certificates, + string $executionRetention, + string $auditRetention, + Log $log + ): void { $payload = $message->getPayload() ?? []; if (empty($payload)) { diff --git a/src/Appwrite/Platform/Workers/Functions.php b/src/Appwrite/Platform/Workers/Functions.php index 1bc67f2e0e..99dbea43b2 100644 --- a/src/Appwrite/Platform/Workers/Functions.php +++ b/src/Appwrite/Platform/Workers/Functions.php @@ -55,8 +55,18 @@ class Functions extends Action ->callback([$this, 'action']); } - public function action(Document $project, Message $message, Database $dbForProject, Webhook $queueForWebhooks, Func $queueForFunctions, Realtime $queueForRealtime, Event $queueForEvents, StatsUsage $queueForStatsUsage, Log $log, callable $isResourceBlocked): void - { + public function action( + Document $project, + Message $message, + Database $dbForProject, + Webhook $queueForWebhooks, + Func $queueForFunctions, + Realtime $queueForRealtime, + Event $queueForEvents, + StatsUsage $queueForStatsUsage, + Log $log, + callable $isResourceBlocked + ): void { $payload = $message->getPayload() ?? []; if (empty($payload)) { diff --git a/src/Appwrite/Platform/Workers/Migrations.php b/src/Appwrite/Platform/Workers/Migrations.php index f683c268a0..6b8543f299 100644 --- a/src/Appwrite/Platform/Workers/Migrations.php +++ b/src/Appwrite/Platform/Workers/Migrations.php @@ -34,6 +34,9 @@ class Migrations extends Action protected Document $project; + /** + * @var callable + */ protected $logError; public static function getName(): string diff --git a/src/Appwrite/Platform/Workers/StatsResources.php b/src/Appwrite/Platform/Workers/StatsResources.php index ef0b24b5c3..dae2967264 100644 --- a/src/Appwrite/Platform/Workers/StatsResources.php +++ b/src/Appwrite/Platform/Workers/StatsResources.php @@ -114,6 +114,13 @@ class StatsResources extends Action $keys = $dbForPlatform->count('keys', [ Query::equal('projectInternalId', [$project->getInternalId()]) ]); + + $domains = $dbForPlatform->count('rules', [ + Query::equal('projectInternalId', [$project->getInternalId()]), + Query::equal('owner', ['']), + ]); + + $databases = $dbForProject->count('databases'); $buckets = $dbForProject->count('buckets'); $users = $dbForProject->count('users'); @@ -162,6 +169,7 @@ class StatsResources extends Action METRIC_PROVIDERS => $providers, METRIC_TOPICS => $topics, METRIC_KEYS => $keys, + METRIC_DOMAINS => $domains, METRIC_TARGETS => $targets, str_replace('{providerType}', MESSAGE_TYPE_EMAIL, METRIC_PROVIDER_TYPE_TARGETS) => $emailTargets, str_replace('{providerType}', MESSAGE_TYPE_PUSH, METRIC_PROVIDER_TYPE_TARGETS) => $pushTargets, diff --git a/src/Appwrite/Platform/Workers/StatsUsage.php b/src/Appwrite/Platform/Workers/StatsUsage.php index a755f723a0..1d270fcd04 100644 --- a/src/Appwrite/Platform/Workers/StatsUsage.php +++ b/src/Appwrite/Platform/Workers/StatsUsage.php @@ -152,7 +152,11 @@ class StatsUsage extends Action case str_starts_with($document->getCollection(), 'database_') && !str_contains($document->getCollection(), 'collection'): //collections $parts = explode('_', $document->getCollection()); $databaseInternalId = $parts[1] ?? 0; - $documents = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$databaseInternalId, $document->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS))); + $documents = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace( + ['{databaseInternalId}', '{collectionInternalId}'], + [$databaseInternalId, $document->getInternalId()], + METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS + ))); if (!empty($documents['value'])) { $metrics[] = [ diff --git a/src/Appwrite/Utopia/Response/Model/Deployment.php b/src/Appwrite/Utopia/Response/Model/Deployment.php index 4729e8a122..55c1589af0 100644 --- a/src/Appwrite/Utopia/Response/Model/Deployment.php +++ b/src/Appwrite/Utopia/Response/Model/Deployment.php @@ -96,7 +96,7 @@ class Deployment extends Model ]) ->addRule('status', [ 'type' => self::TYPE_STRING, - 'description' => 'The deployment status. Possible values are "waiting", "processing", "building", "waiting", "ready", and "failed".', + 'description' => 'The deployment status. Possible values are "waiting", "processing", "building", "ready", and "failed".', 'default' => '', 'example' => 'ready', ]) diff --git a/src/Appwrite/Utopia/Response/Model/FrameworkAdapter.php b/src/Appwrite/Utopia/Response/Model/FrameworkAdapter.php index 8348f3a6d5..afbd78eda4 100644 --- a/src/Appwrite/Utopia/Response/Model/FrameworkAdapter.php +++ b/src/Appwrite/Utopia/Response/Model/FrameworkAdapter.php @@ -34,6 +34,12 @@ class FrameworkAdapter extends Model 'default' => '', 'example' => './dist', ]) + ->addRule('fallbackFile', [ + 'type' => self::TYPE_STRING, + 'description' => 'Name of fallback file to use instead of 404 page. If null, Appwrite 404 page will be displayed.', + 'default' => null, + 'example' => 'index.html', + ]) ; } diff --git a/src/Appwrite/Utopia/Response/Model/Func.php b/src/Appwrite/Utopia/Response/Model/Func.php index 5d937ab075..e0929684fb 100644 --- a/src/Appwrite/Utopia/Response/Model/Func.php +++ b/src/Appwrite/Utopia/Response/Model/Func.php @@ -77,6 +77,24 @@ class Func extends Model 'default' => '', 'example' => self::TYPE_DATETIME_EXAMPLE, ]) + ->addRule('latestDeploymentId', [ + 'type' => self::TYPE_STRING, + 'description' => 'Function\'s latest deployment ID.', + 'default' => '', + 'example' => '5e5ea5c16897e', + ]) + ->addRule('latestDeploymentCreatedAt', [ + 'type' => self::TYPE_DATETIME, + 'description' => 'Latest deployment creation date in ISO 8601 format.', + 'default' => '', + 'example' => self::TYPE_DATETIME_EXAMPLE, + ]) + ->addRule('latestDeploymentStatus', [ + 'type' => self::TYPE_STRING, + 'description' => 'Status of latest deployment. Possible values are "waiting", "processing", "building", "ready", and "failed".', + 'default' => '', + 'example' => 'ready', + ]) ->addRule('scopes', [ 'type' => self::TYPE_STRING, 'description' => 'Allowed permission scopes.', diff --git a/src/Appwrite/Utopia/Response/Model/Site.php b/src/Appwrite/Utopia/Response/Model/Site.php index fad4856f3e..478b7881b3 100644 --- a/src/Appwrite/Utopia/Response/Model/Site.php +++ b/src/Appwrite/Utopia/Response/Model/Site.php @@ -76,6 +76,24 @@ class Site extends Model 'default' => '', 'example' => '5e5ea5c16897e', ]) + ->addRule('latestDeploymentId', [ + 'type' => self::TYPE_STRING, + 'description' => 'Site\'s latest deployment ID.', + 'default' => '', + 'example' => '5e5ea5c16897e', + ]) + ->addRule('latestDeploymentCreatedAt', [ + 'type' => self::TYPE_DATETIME, + 'description' => 'Latest deployment creation date in ISO 8601 format.', + 'default' => '', + 'example' => self::TYPE_DATETIME_EXAMPLE, + ]) + ->addRule('latestDeploymentStatus', [ + 'type' => self::TYPE_STRING, + 'description' => 'Status of latest deployment. Possible values are "waiting", "processing", "building", "ready", and "failed".', + 'default' => '', + 'example' => 'ready', + ]) ->addRule('vars', [ 'type' => Response::MODEL_VARIABLE, 'description' => 'Site variables.', diff --git a/tests/e2e/Scopes/ProjectCustom.php b/tests/e2e/Scopes/ProjectCustom.php index f8f779027b..a1616f2f74 100644 --- a/tests/e2e/Scopes/ProjectCustom.php +++ b/tests/e2e/Scopes/ProjectCustom.php @@ -4,6 +4,7 @@ namespace Tests\E2E\Scopes; use Tests\E2E\Client; use Utopia\Database\Helpers\ID; +use Utopia\System\System; trait ProjectCustom { @@ -42,7 +43,7 @@ trait ProjectCustom 'x-appwrite-project' => 'console', ], [ 'projectId' => ID::unique(), - 'region' => 'default', + 'region' => System::getEnv('_APP_REGION', 'default'), 'name' => 'Demo Project', 'teamId' => $team['body']['$id'], 'description' => 'Demo Project Description', diff --git a/tests/e2e/Services/Functions/FunctionsBase.php b/tests/e2e/Services/Functions/FunctionsBase.php index 2d82de22f2..475eb9b9b4 100644 --- a/tests/e2e/Services/Functions/FunctionsBase.php +++ b/tests/e2e/Services/Functions/FunctionsBase.php @@ -231,6 +231,16 @@ trait FunctionsBase return $deployment; } + protected function deleteDeployment(string $functionId, string $deploymentId): mixed + { + $deployment = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId . '/deployments/' . $deploymentId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + return $deployment; + } + protected function createTemplateDeployment(string $functionId, mixed $params = []): mixed { $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments/template', array_merge([ diff --git a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php index 86381fa221..79b20571a5 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php @@ -8,6 +8,7 @@ use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\SideClient; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Role; +use Utopia\System\System; class FunctionsCustomClientTest extends Scope { @@ -109,7 +110,7 @@ class FunctionsCustomClientTest extends Scope $this->assertEquals('PHP', $output['APPWRITE_FUNCTION_RUNTIME_NAME']); $this->assertEquals('8.0', $output['APPWRITE_FUNCTION_RUNTIME_VERSION']); $this->assertEquals(APP_VERSION_STABLE, $output['APPWRITE_VERSION']); - $this->assertEquals('default', $output['APPWRITE_REGION']); + $this->assertEquals(System::getEnv('_APP_REGION', 'default'), $output['APPWRITE_REGION']); $this->assertEquals('', $output['APPWRITE_FUNCTION_EVENT']); $this->assertEquals('foobar', $output['APPWRITE_FUNCTION_DATA']); $this->assertEquals($this->getUser()['$id'], $output['APPWRITE_FUNCTION_USER_ID']); @@ -219,7 +220,7 @@ class FunctionsCustomClientTest extends Scope $this->assertEquals('PHP', $output['APPWRITE_FUNCTION_RUNTIME_NAME']); $this->assertEquals('8.0', $output['APPWRITE_FUNCTION_RUNTIME_VERSION']); $this->assertEquals(APP_VERSION_STABLE, $output['APPWRITE_VERSION']); - $this->assertEquals('default', $output['APPWRITE_REGION']); + $this->assertEquals(System::getEnv('_APP_REGION', 'default'), $output['APPWRITE_REGION']); $this->assertEquals('', $output['APPWRITE_FUNCTION_EVENT']); $this->assertEquals('foobar', $output['APPWRITE_FUNCTION_DATA']); $this->assertEquals($this->getUser()['$id'], $output['APPWRITE_FUNCTION_USER_ID']); diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index e13e7f9147..40d9ea5dba 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -1288,8 +1288,7 @@ class FunctionsCustomServerTest extends Scope return [ ['folder' => 'php-fn', 'name' => 'php-8.0', 'entrypoint' => 'index.php', 'runtimeName' => 'PHP', 'runtimeVersion' => '8.0'], ['folder' => 'node', 'name' => 'node-18.0', 'entrypoint' => 'index.js', 'runtimeName' => 'Node.js', 'runtimeVersion' => '18.0'], - // TODO: Re-enable; temporarly disabled due to OPR v5 issues - // ['folder' => 'python', 'name' => 'python-3.9', 'entrypoint' => 'main.py', 'runtimeName' => 'Python', 'runtimeVersion' => '3.9'], + ['folder' => 'python', 'name' => 'python-3.9', 'entrypoint' => 'main.py', 'runtimeName' => 'Python', 'runtimeVersion' => '3.9'], ['folder' => 'ruby', 'name' => 'ruby-3.1', 'entrypoint' => 'main.rb', 'runtimeName' => 'Ruby', 'runtimeVersion' => '3.1'], // Swift and Dart disabled on purpose, as it's very slow. // [ 'folder' => 'dart', 'name' => 'dart-2.15', 'entrypoint' => 'main.dart', 'runtimeName' => 'Dart', 'runtimeVersion' => '2.15' ], @@ -2101,12 +2100,27 @@ class FunctionsCustomServerTest extends Scope ]); $this->assertNotEmpty($functionId); + + $function = $this->getFunction($functionId); + $this->assertEquals(200, $function['headers']['status-code']); + $this->assertArrayHasKey('latestDeploymentId', $function['body']); + $this->assertArrayHasKey('latestDeploymentCreatedAt', $function['body']); + $this->assertArrayHasKey('latestDeploymentStatus', $function['body']); + $this->assertEmpty($function['body']['latestDeploymentId']); + $this->assertEmpty($function['body']['latestDeploymentCreatedAt']); + $this->assertEmpty($function['body']['latestDeploymentStatus']); + $deploymentId1 = $this->setupDeployment($functionId, [ 'code' => $this->packageFunction('php-cookie'), 'activate' => true ]); $this->assertNotEmpty($deploymentId1); + $function = $this->getFunction($functionId); + $this->assertEquals(200, $function['headers']['status-code']); + $this->assertEquals($deploymentId1, $function['body']['latestDeploymentId']); + $this->assertEquals('ready', $function['body']['latestDeploymentStatus']); + $execution = $this->createExecution($functionId, [ 'headers' => [ 'cookie' => 'cookieName=cookieValue' ] ]); @@ -2120,6 +2134,11 @@ class FunctionsCustomServerTest extends Scope ]); $this->assertNotEmpty($deploymentId2); + $function = $this->getFunction($functionId); + $this->assertEquals(200, $function['headers']['status-code']); + $this->assertEquals($deploymentId2, $function['body']['latestDeploymentId']); + $this->assertEquals('ready', $function['body']['latestDeploymentStatus']); + $execution = $this->createExecution($functionId); $this->assertEquals(201, $execution['headers']['status-code']); $this->assertNotEmpty($execution['body']['$id']); @@ -2128,6 +2147,8 @@ class FunctionsCustomServerTest extends Scope $function = $this->getFunction($functionId); $this->assertEquals(200, $function['headers']['status-code']); $this->assertEquals($deploymentId2, $function['body']['deploymentId']); + $this->assertEquals($deploymentId2, $function['body']['latestDeploymentId']); + $this->assertEquals('ready', $function['body']['latestDeploymentStatus']); $function = $this->updateFunctionDeployment($functionId, $deploymentId1); $this->assertEquals(200, $function['headers']['status-code']); @@ -2136,6 +2157,8 @@ class FunctionsCustomServerTest extends Scope $function = $this->getFunction($functionId); $this->assertEquals(200, $function['headers']['status-code']); $this->assertEquals($deploymentId1, $function['body']['deploymentId']); + $this->assertEquals($deploymentId2, $function['body']['latestDeploymentId']); + $this->assertEquals('ready', $function['body']['latestDeploymentStatus']); $execution = $this->createExecution($functionId, [ 'headers' => [ 'cookie' => 'cookieName=cookieValue' ] @@ -2144,6 +2167,14 @@ class FunctionsCustomServerTest extends Scope $this->assertNotEmpty($execution['body']['$id']); $this->assertStringContainsString('cookieValue', $execution['body']['responseBody']); + $deployment = $this->deleteDeployment($functionId, $deploymentId2); + $this->assertEquals(204, $deployment['headers']['status-code']); + + $function = $this->getFunction($functionId); + $this->assertEquals(200, $function['headers']['status-code']); + $this->assertEquals($deploymentId1, $function['body']['latestDeploymentId']); + $this->assertEquals('ready', $function['body']['latestDeploymentStatus']); + $this->cleanupFunction($functionId); } } diff --git a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php index ed9171e46a..afd1189496 100644 --- a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php +++ b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php @@ -14,6 +14,7 @@ use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Helpers\ID; use Utopia\Database\Query; +use Utopia\System\System; class ProjectsConsoleClientTest extends Scope { @@ -49,7 +50,7 @@ class ProjectsConsoleClientTest extends Scope 'projectId' => ID::unique(), 'name' => 'Project Test', 'teamId' => $team['body']['$id'], - 'region' => 'default', + 'region' => System::getEnv('_APP_REGION', 'default') ]); $this->assertEquals(201, $response['headers']['status-code']); @@ -89,7 +90,7 @@ class ProjectsConsoleClientTest extends Scope 'projectId' => ID::unique(), 'name' => '', 'teamId' => $team['body']['$id'], - 'region' => 'default' + 'region' => System::getEnv('_APP_REGION', 'default') ]); $this->assertEquals(400, $response['headers']['status-code']); @@ -100,7 +101,7 @@ class ProjectsConsoleClientTest extends Scope ], $this->getHeaders()), [ 'projectId' => ID::unique(), 'name' => 'Project Test', - 'region' => 'default' + 'region' => System::getEnv('_APP_REGION', 'default') ]); $this->assertEquals(401, $response['headers']['status-code']); @@ -129,7 +130,7 @@ class ProjectsConsoleClientTest extends Scope 'projectId' => $projectId, 'name' => 'Project Duplicate', 'teamId' => $teamId, - 'region' => 'default' + 'region' => System::getEnv('_APP_REGION', 'default') ]); $this->assertEquals(409, $response['headers']['status-code']); @@ -178,7 +179,7 @@ class ProjectsConsoleClientTest extends Scope 'projectId' => ID::unique(), 'name' => 'Team 1 Project', 'teamId' => $team1, - 'region' => 'default', + 'region' => System::getEnv('_APP_REGION', 'default'), ]); $this->assertEquals(201, $response['headers']['status-code']); @@ -277,7 +278,7 @@ class ProjectsConsoleClientTest extends Scope 'projectId' => ID::unique(), 'name' => 'Project Test 2', 'teamId' => $team['body']['$id'], - 'region' => 'default' + 'region' => System::getEnv('_APP_REGION', 'default') ]); $this->assertEquals(201, $response['headers']['status-code']); @@ -2042,7 +2043,7 @@ class ProjectsConsoleClientTest extends Scope 'projectId' => ID::unique(), 'name' => 'Project Test', 'teamId' => $team['body']['$id'], - 'region' => 'default' + 'region' => System::getEnv('_APP_REGION', 'default') ]); $this->assertEquals(201, $project['headers']['status-code']); @@ -2135,7 +2136,7 @@ class ProjectsConsoleClientTest extends Scope 'projectId' => ID::unique(), 'name' => 'Project Test', 'teamId' => $team['body']['$id'], - 'region' => 'default' + 'region' => System::getEnv('_APP_REGION', 'default') ]); $this->assertEquals(201, $project['headers']['status-code']); @@ -3749,7 +3750,7 @@ class ProjectsConsoleClientTest extends Scope 'projectId' => ID::unique(), 'name' => 'Amazing Project', 'teamId' => $teamId, - 'region' => 'default' + 'region' => System::getEnv('_APP_REGION', 'default') ]); $this->assertEquals(201, $project['headers']['status-code']); @@ -3820,7 +3821,7 @@ class ProjectsConsoleClientTest extends Scope 'projectId' => ID::unique(), 'name' => 'Amazing Project 1', 'teamId' => $teamId, - 'region' => 'default' + 'region' => System::getEnv('_APP_REGION', 'default') ]); $project2 = $this->client->call(Client::METHOD_POST, '/projects', array_merge([ @@ -3830,7 +3831,7 @@ class ProjectsConsoleClientTest extends Scope 'projectId' => ID::unique(), 'name' => 'Amazing Project 2', 'teamId' => $teamId, - 'region' => 'default' + 'region' => System::getEnv('_APP_REGION', 'default') ]); $project1Id = $project1['body']['$id']; diff --git a/tests/e2e/Services/Sites/SitesBase.php b/tests/e2e/Services/Sites/SitesBase.php index abfce68d89..ff7e4b283f 100644 --- a/tests/e2e/Services/Sites/SitesBase.php +++ b/tests/e2e/Services/Sites/SitesBase.php @@ -244,6 +244,16 @@ trait SitesBase return $deployment; } + protected function deleteDeployment(string $siteId, string $deploymentId): mixed + { + $deployment = $this->client->call(Client::METHOD_DELETE, '/sites/' . $siteId . '/deployments/' . $deploymentId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + return $deployment; + } + protected function setupDuplicateDeployment(string $siteId, string $deploymentId): string { $deployment = $this->createDuplicateDeployment($siteId, $deploymentId); diff --git a/tests/e2e/Services/Sites/SitesCustomServerTest.php b/tests/e2e/Services/Sites/SitesCustomServerTest.php index ac6a74aeb5..a6669d1e38 100644 --- a/tests/e2e/Services/Sites/SitesCustomServerTest.php +++ b/tests/e2e/Services/Sites/SitesCustomServerTest.php @@ -2057,6 +2057,15 @@ class SitesCustomServerTest extends Scope ]); $this->assertNotEmpty($siteId); + $site = $this->getSite($siteId); + $this->assertEquals(200, $site['headers']['status-code']); + $this->assertArrayHasKey('latestDeploymentId', $site['body']); + $this->assertArrayHasKey('latestDeploymentCreatedAt', $site['body']); + $this->assertArrayHasKey('latestDeploymentStatus', $site['body']); + $this->assertEmpty($site['body']['latestDeploymentId']); + $this->assertEmpty($site['body']['latestDeploymentCreatedAt']); + $this->assertEmpty($site['body']['latestDeploymentStatus']); + $domain = $this->setupSiteDomain($siteId); $this->assertNotEmpty($domain); $proxyClient = new Client(); @@ -2068,6 +2077,11 @@ class SitesCustomServerTest extends Scope ]); $this->assertNotEmpty($deploymentId1); + $site = $this->getSite($siteId); + $this->assertEquals(200, $site['headers']['status-code']); + $this->assertEquals($deploymentId1, $site['body']['latestDeploymentId']); + $this->assertEquals('ready', $site['body']['latestDeploymentStatus']); + $response = $proxyClient->call(Client::METHOD_GET, '/'); $this->assertEquals(200, $response['headers']['status-code']); $this->assertStringContainsString('Hello Appwrite', $response['body']); @@ -2078,26 +2092,43 @@ class SitesCustomServerTest extends Scope ]); $this->assertNotEmpty($deploymentId2); + $site = $this->getSite($siteId); + $this->assertEquals(200, $site['headers']['status-code']); + $this->assertEquals($deploymentId2, $site['body']['latestDeploymentId']); + $this->assertEquals('ready', $site['body']['latestDeploymentStatus']); + $response = $proxyClient->call(Client::METHOD_GET, '/'); $this->assertEquals(200, $response['headers']['status-code']); $this->assertStringContainsString('Index page', $response['body']); - $function = $this->getSite($siteId); - $this->assertEquals(200, $function['headers']['status-code']); - $this->assertEquals($deploymentId2, $function['body']['deploymentId']); + $site = $this->getSite($siteId); + $this->assertEquals(200, $site['headers']['status-code']); + $this->assertEquals($deploymentId2, $site['body']['deploymentId']); + $this->assertEquals($deploymentId2, $site['body']['latestDeploymentId']); + $this->assertEquals('ready', $site['body']['latestDeploymentStatus']); - $function = $this->updateSiteDeployment($siteId, $deploymentId1); - $this->assertEquals(200, $function['headers']['status-code']); - $this->assertEquals($deploymentId1, $function['body']['deploymentId']); + $site = $this->updateSiteDeployment($siteId, $deploymentId1); + $this->assertEquals(200, $site['headers']['status-code']); + $this->assertEquals($deploymentId1, $site['body']['deploymentId']); - $function = $this->getSite($siteId); - $this->assertEquals(200, $function['headers']['status-code']); - $this->assertEquals($deploymentId1, $function['body']['deploymentId']); + $site = $this->getSite($siteId); + $this->assertEquals(200, $site['headers']['status-code']); + $this->assertEquals($deploymentId1, $site['body']['deploymentId']); + $this->assertEquals($deploymentId2, $site['body']['latestDeploymentId']); + $this->assertEquals('ready', $site['body']['latestDeploymentStatus']); $response = $proxyClient->call(Client::METHOD_GET, '/'); $this->assertEquals(200, $response['headers']['status-code']); $this->assertStringContainsString('Hello Appwrite', $response['body']); + $deployment = $this->deleteDeployment($siteId, $deploymentId2); + $this->assertEquals(204, $deployment['headers']['status-code']); + + $site = $this->getSite($siteId); + $this->assertEquals(200, $site['headers']['status-code']); + $this->assertEquals($deploymentId1, $site['body']['latestDeploymentId']); + $this->assertEquals('ready', $site['body']['latestDeploymentStatus']); + $this->cleanupSite($siteId); }