From 82c612bad97b5f8824541956e432f8555e8539c3 Mon Sep 17 00:00:00 2001 From: Daniel Lockyer Date: Wed, 8 May 2024 09:17:41 +0200 Subject: [PATCH] Rolled out API framework JSDoc typing to more places - this updates a bunch of places where we're just using Object to cheat the system - doing this means editor autocomplete and basic type checking is better because we now have proper types in place - functionality should not change, these are just comments --- ghost/api-framework/lib/headers.js | 4 ++-- ghost/api-framework/lib/http.js | 3 ++- ghost/api-framework/lib/pipeline.js | 22 +++++++++---------- ghost/api-framework/lib/serializers/handle.js | 4 ++-- ghost/api-framework/lib/validators/handle.js | 2 +- .../api-framework/lib/validators/input/all.js | 16 ++++++++++++++ .../core/server/api/endpoints/utils/index.js | 10 ++++++++- .../server/api/endpoints/utils/permissions.js | 4 ++-- .../utils/serializers/output/members.js | 15 +++++-------- .../utils/serializers/output/settings.js | 6 +++-- .../utils/serializers/output/tiers.js | 5 +---- .../utils/validators/utils/json-schema.js | 3 +-- .../services/comments/CommentsController.js | 2 +- ghost/posts-service/lib/PostsService.js | 2 +- 14 files changed, 58 insertions(+), 40 deletions(-) diff --git a/ghost/api-framework/lib/headers.js b/ghost/api-framework/lib/headers.js index f235e82906..165baeb377 100644 --- a/ghost/api-framework/lib/headers.js +++ b/ghost/api-framework/lib/headers.js @@ -99,8 +99,8 @@ module.exports = { * * @param {Object} result - API response * @param {Object} apiConfigHeaders - * @param {Object} frame - * @return {Promise} + * @param {import('@tryghost/api-framework').Frame} frame + * @return {Promise} */ async get(result, apiConfigHeaders = {}, frame) { let headers = {}; diff --git a/ghost/api-framework/lib/http.js b/ghost/api-framework/lib/http.js index 65188185f8..cf35d70559 100644 --- a/ghost/api-framework/lib/http.js +++ b/ghost/api-framework/lib/http.js @@ -10,7 +10,7 @@ const headers = require('./headers'); * This wrapper is used in the routes definition (see web/). * The wrapper receives the express request, prepares the frame and forwards the request to the pipeline. * - * @param {Function} apiImpl - Pipeline wrapper, which executes the target ctrl function. + * @param {import('@tryghost/api-framework').Controller} apiImpl - Pipeline wrapper, which executes the target ctrl function. * @return {import('express').RequestHandler} */ const http = (apiImpl) => { @@ -18,6 +18,7 @@ const http = (apiImpl) => { * @param {import('express').Request} req - Express request object. * @param {import('express').Response} res - Express response object. * @param {import('express').NextFunction} next - Express next function. + * @returns {Promise} */ return async function Http(req, res, next) { debug(`External API request to ${req.url}`); diff --git a/ghost/api-framework/lib/pipeline.js b/ghost/api-framework/lib/pipeline.js index bf60b5d03b..a2dfa48440 100644 --- a/ghost/api-framework/lib/pipeline.js +++ b/ghost/api-framework/lib/pipeline.js @@ -20,8 +20,8 @@ const STAGES = { * * @param {Object} apiUtils - Local utils of target API version. * @param {Object} apiConfig - Docname & Method of ctrl. - * @param {Object} apiImpl - Controller configuration. - * @param {Object} frame + * @param {import('@tryghost/api-framework').ControllerMethod} apiImpl - Controller configuration. + * @param {import('@tryghost/api-framework').Frame} frame * @return {Promise} */ input(apiUtils, apiConfig, apiImpl, frame) { @@ -57,8 +57,8 @@ const STAGES = { * * @param {Object} apiUtils - Local utils of target API version. * @param {Object} apiConfig - Docname & Method of ctrl. - * @param {Object} apiImpl - Controller configuration. - * @param {Object} frame + * @param {import('@tryghost/api-framework').ControllerMethod} apiImpl - Controller configuration. + * @param {import('@tryghost/api-framework').Frame} frame * @return {Promise} */ input(apiUtils, apiConfig, apiImpl, frame) { @@ -80,8 +80,8 @@ const STAGES = { * * @param {Object} apiUtils - Local utils of target API version. * @param {Object} apiConfig - Docname & Method of ctrl. - * @param {Object} apiImpl - Controller configuration. - * @param {Object} frame + * @param {import('@tryghost/api-framework').ControllerMethod} apiImpl - Controller configuration. + * @param {import('@tryghost/api-framework').Frame} frame * @return {Promise} */ output(response, apiUtils, apiConfig, apiImpl, frame) { @@ -99,8 +99,8 @@ const STAGES = { * * @param {Object} apiUtils - Local utils of target API version. * @param {Object} apiConfig - Docname & Method of ctrl. - * @param {Object} apiImpl - Controller configuration. - * @param {Object} frame + * @param {import('@tryghost/api-framework').ControllerMethod} apiImpl - Controller configuration. + * @param {import('@tryghost/api-framework').Frame} frame * @return {Promise} */ permissions(apiUtils, apiConfig, apiImpl, frame) { @@ -145,8 +145,8 @@ const STAGES = { * * @param {Object} apiUtils - Local utils of target API version. * @param {Object} apiConfig - Docname & Method of ctrl. - * @param {Object} apiImpl - Controller configuration. - * @param {Object} frame + * @param {import('@tryghost/api-framework').ControllerMethod} apiImpl - Controller configuration. + * @param {import('@tryghost/api-framework').Frame} frame * @return {Promise} */ query(apiUtils, apiConfig, apiImpl, frame) { @@ -176,7 +176,7 @@ const controllerMap = new Map(); * 4. Controller - Execute the controller implementation & receive model response. * 5. Output Serialisation - Output formatting, Deprecations, Extra attributes etc... * - * @param {Object} apiController + * @param {import('@tryghost/api-framework').Controller} apiController * @param {Object} apiUtils - Local utils (validation & serialisation) from target API version * @param {String} [apiType] - Content or Admin API access * @return {Object} diff --git a/ghost/api-framework/lib/serializers/handle.js b/ghost/api-framework/lib/serializers/handle.js index c86fe1bdfa..6356ebdbf3 100644 --- a/ghost/api-framework/lib/serializers/handle.js +++ b/ghost/api-framework/lib/serializers/handle.js @@ -12,7 +12,7 @@ const errors = require('@tryghost/errors'); * * @param {Object} apiConfig - Docname + method of the ctrl * @param {Object} apiSerializers - Target API serializers - * @param {Object} frame + * @param {import('@tryghost/api-framework').Frame} frame */ module.exports.input = (apiConfig, apiSerializers, frame) => { debug('input'); @@ -90,7 +90,7 @@ const getBestMatchSerializer = function (apiSerializers, docName, method) { * @param {Object} response - API response * @param {Object} apiConfig - Docname + method of the ctrl * @param {Object} apiSerializers - Target API serializers - * @param {Object} frame + * @param {import('@tryghost/api-framework').Frame} frame */ module.exports.output = (response = {}, apiConfig, apiSerializers, frame) => { debug('output'); diff --git a/ghost/api-framework/lib/validators/handle.js b/ghost/api-framework/lib/validators/handle.js index ab9901ea9f..43f62f77dd 100644 --- a/ghost/api-framework/lib/validators/handle.js +++ b/ghost/api-framework/lib/validators/handle.js @@ -12,7 +12,7 @@ const {sequence} = require('@tryghost/promise'); * * @param {Object} apiConfig - Docname + method of the ctrl * @param {Object} apiValidators - Target API validators - * @param {Object} frame + * @param {import('@tryghost/api-framework').Frame} frame */ module.exports.input = (apiConfig, apiValidators, frame) => { debug('input begin'); diff --git a/ghost/api-framework/lib/validators/input/all.js b/ghost/api-framework/lib/validators/input/all.js index f6e4efc664..fadb17bb2c 100644 --- a/ghost/api-framework/lib/validators/input/all.js +++ b/ghost/api-framework/lib/validators/input/all.js @@ -90,6 +90,10 @@ const validate = (config, attrs) => { }; module.exports = { + /** + * @param {object} apiConfig + * @param {import('@tryghost/api-framework').Frame} frame + */ all(apiConfig, frame) { debug('validate all'); @@ -102,6 +106,10 @@ module.exports = { return Promise.resolve(); }, + /** + * @param {object} apiConfig + * @param {import('@tryghost/api-framework').Frame} frame + */ browse(apiConfig, frame) { debug('validate browse'); @@ -121,6 +129,10 @@ module.exports = { return this.browse(...arguments); }, + /** + * @param {object} apiConfig + * @param {import('@tryghost/api-framework').Frame} frame + */ add(apiConfig, frame) { debug('validate add'); @@ -168,6 +180,10 @@ module.exports = { } }, + /** + * @param {object} apiConfig + * @param {import('@tryghost/api-framework').Frame} frame + */ edit(apiConfig, frame) { debug('validate edit'); const result = this.add(...arguments); diff --git a/ghost/core/core/server/api/endpoints/utils/index.js b/ghost/core/core/server/api/endpoints/utils/index.js index 221dd20daf..50c6718a82 100644 --- a/ghost/core/core/server/api/endpoints/utils/index.js +++ b/ghost/core/core/server/api/endpoints/utils/index.js @@ -19,17 +19,25 @@ module.exports = { * is a Content API implementation - see index.js file. * * @TODO: Move this helper function into a utils.js file. - * @param {Object} frame + * @param {import('@tryghost/api-framework').Frame} frame * @return {boolean} */ isContentAPI: (frame) => { return frame.apiType === 'content'; }, + /** + * @param {import('@tryghost/api-framework').Frame} frame + * @returns {boolean} + */ isMembersAPI: (frame) => { return frame.apiType === 'members'; }, + /** + * @param {import('@tryghost/api-framework').Frame} frame + * @returns {boolean} + */ isInternal: (frame) => { return frame.options.context && frame.options.context.internal; } diff --git a/ghost/core/core/server/api/endpoints/utils/permissions.js b/ghost/core/core/server/api/endpoints/utils/permissions.js index b6d2934377..9379f212e6 100644 --- a/ghost/core/core/server/api/endpoints/utils/permissions.js +++ b/ghost/core/core/server/api/endpoints/utils/permissions.js @@ -12,7 +12,7 @@ const messages = { * @description Handle requests, which need authentication. * * @param {Object} apiConfig - Docname & method of API ctrl - * @param {Object} frame + * @param {import('@tryghost/api-framework').Frame} frame * @return {Promise} */ const nonePublicAuth = (apiConfig, frame) => { @@ -81,7 +81,7 @@ module.exports = { * @description Handle permission stage for API. * * @param {Object} apiConfig - Docname & method of target ctrl. - * @param {Object} frame + * @param {import('@tryghost/api-framework').Frame} frame * @return {Promise} */ handle(apiConfig, frame) { diff --git a/ghost/core/core/server/api/endpoints/utils/serializers/output/members.js b/ghost/core/core/server/api/endpoints/utils/serializers/output/members.js index 0dcb1da3e7..799c9b0564 100644 --- a/ghost/core/core/server/api/endpoints/utils/serializers/output/members.js +++ b/ghost/core/core/server/api/endpoints/utils/serializers/output/members.js @@ -28,7 +28,7 @@ module.exports = { * * @param {{data: import('bookshelf').Model[], meta: PageMeta}} page * @param {APIConfig} _apiConfig - * @param {Frame} frame + * @param {import('@tryghost/api-framework').Frame} frame * * @returns {{members: SerializedMember[], meta: PageMeta}} */ @@ -42,7 +42,7 @@ function paginatedMembers(page, _apiConfig, frame) { /** * @param {import('bookshelf').Model} model * @param {APIConfig} _apiConfig - * @param {Frame} frame + * @param {import('@tryghost/api-framework').Frame} frame * * @returns {{members: SerializedMember[]}} */ @@ -55,7 +55,7 @@ function singleMember(model, _apiConfig, frame) { /** * @param {object} bulkActionResult * @param {APIConfig} _apiConfig - * @param {Frame} frame + * @param {import('@tryghost/api-framework').Frame} frame * * @returns {{bulk: SerializedBulkAction}} */ @@ -219,9 +219,9 @@ function passthrough(data) { * @template Data * @template Response * @param {string} debugString - * @param {(data: Data, apiConfig: APIConfig, frame: Frame) => Response} serialize - A function to serialize the data into an object suitable for API response + * @param {(data: Data, apiConfig: APIConfig, frame: import('@tryghost/api-framework').Frame) => Response} serialize * - * @returns {(data: Data, apiConfig: APIConfig, frame: Frame) => void} + * @returns {(data: Data, apiConfig: APIConfig, frame: import('@tryghost/api-framework').Frame) => void} */ function createSerializer(debugString, serialize) { return function serializer(data, apiConfig, frame) { @@ -355,8 +355,3 @@ function createSerializer(debugString, serialize) { * @prop {string} docName * @prop {string} method */ - -/** - * @typedef {Object} Frame - * @prop {Object} options - */ diff --git a/ghost/core/core/server/api/endpoints/utils/serializers/output/settings.js b/ghost/core/core/server/api/endpoints/utils/serializers/output/settings.js index 9a8c47f51a..63b89fd3f1 100644 --- a/ghost/core/core/server/api/endpoints/utils/serializers/output/settings.js +++ b/ghost/core/core/server/api/endpoints/utils/serializers/output/settings.js @@ -2,6 +2,8 @@ const _ = require('lodash'); const utils = require('../../index'); const mappers = require('./mappers'); +/** @typedef {import('@tryghost/api-framework').Frame} Frame */ + /** * Filters an object based on a given filter object * @private @@ -25,7 +27,7 @@ function settingsFilter(settings, filter) { * * @param {Object} models * @param {Object} apiConfig - * @param {Object} frame + * @param {Frame} frame */ function serializeSettings(models, apiConfig, frame) { let filteredSettings; @@ -79,7 +81,7 @@ function passthrough(data) { * @template Data * @param {Data} data * @param {Object} apiConfig - * @param {Object} frame + * @param {Frame} frame */ function serializeData(data, apiConfig, frame) { frame.response = data; diff --git a/ghost/core/core/server/api/endpoints/utils/serializers/output/tiers.js b/ghost/core/core/server/api/endpoints/utils/serializers/output/tiers.js index 09fe71db4b..db475c9573 100644 --- a/ghost/core/core/server/api/endpoints/utils/serializers/output/tiers.js +++ b/ghost/core/core/server/api/endpoints/utils/serializers/output/tiers.js @@ -131,7 +131,4 @@ function createSerializer(debugString, serialize) { * @prop {string} method */ -/** - * @typedef {Object} Frame - * @prop {Object} options - */ +/** @typedef {import('@tryghost/api-framework').Frame} Frame */ diff --git a/ghost/core/core/server/api/endpoints/utils/validators/utils/json-schema.js b/ghost/core/core/server/api/endpoints/utils/validators/utils/json-schema.js index 539686157b..3cf273f10f 100644 --- a/ghost/core/core/server/api/endpoints/utils/validators/utils/json-schema.js +++ b/ghost/core/core/server/api/endpoints/utils/validators/utils/json-schema.js @@ -5,8 +5,7 @@ const jsonSchema = require('@tryghost/admin-api-schema'); * @param {Object} apiConfig "frame" api configuration object * @param {string} apiConfig.docName the name of the resource * @param {string} apiConfig.method API's method name - * @param {Object} frame "frame" object with data attached to it - * @param {Object} frame.data request data to validate + * @param {import('@tryghost/api-framework').Frame} frame "frame" object with data attached to it */ const validate = async (apiConfig, frame) => await jsonSchema.validate({ data: frame.data, diff --git a/ghost/core/core/server/services/comments/CommentsController.js b/ghost/core/core/server/services/comments/CommentsController.js index b6f902fea7..4a5a4828f0 100644 --- a/ghost/core/core/server/services/comments/CommentsController.js +++ b/ghost/core/core/server/services/comments/CommentsController.js @@ -2,7 +2,7 @@ const _ = require('lodash'); const errors = require('@tryghost/errors'); /** - * @typedef {import('../../api/shared/frame')} Frame + * @typedef {import('@tryghost/api-framework').Frame} Frame */ const {MethodNotAllowedError} = require('@tryghost/errors'); diff --git a/ghost/posts-service/lib/PostsService.js b/ghost/posts-service/lib/PostsService.js index 3bc97b19d1..b225a1151b 100644 --- a/ghost/posts-service/lib/PostsService.js +++ b/ghost/posts-service/lib/PostsService.js @@ -110,7 +110,7 @@ class PostsService { /** * - * @param {any} frame + * @param {import('@tryghost/api-framework').Frame} frame * @param {object} [options] * @param {(event: EventString, dto: any) => Promise | void} [options.eventHandler] - Called before the editPost method resolves with an event string * @returns