From 51d27f3bcaacefda78dc033021b6a85152ab972e Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 1 Jun 2020 14:53:59 +0200 Subject: GTAD parts: new home and format updates for GTAD 0.7 All GTAD-related files (gtad.yaml and templates) from now live in their dedicated gtad/ directory - this helps against removing them accidentally along with the rest of the generated files. The format to list generated files in gtad.yaml has changed a bit before GTAD 0.7 beta2; gtad.yaml in this commit conforms to the new structure. --- gtad/gtad.yaml | 169 +++++++++++++++++++++++++++++++++++++++++ gtad/preamble.mustache | 3 + gtad/template.cpp.mustache | 182 +++++++++++++++++++++++++++++++++++++++++++++ gtad/template.h.mustache | 119 +++++++++++++++++++++++++++++ 4 files changed, 473 insertions(+) create mode 100644 gtad/gtad.yaml create mode 100644 gtad/preamble.mustache create mode 100644 gtad/template.cpp.mustache create mode 100644 gtad/template.h.mustache (limited to 'gtad') diff --git a/gtad/gtad.yaml b/gtad/gtad.yaml new file mode 100644 index 00000000..51f9e26b --- /dev/null +++ b/gtad/gtad.yaml @@ -0,0 +1,169 @@ +analyzer: + subst: + "%CLIENT_RELEASE_LABEL%": r0 + "%CLIENT_MAJOR_VERSION%": r0 + identifiers: + signed: signedData + unsigned: unsignedData + PushRule/default: isDefault + default: defaultVersion # getCapabilities/RoomVersionsCapability + origin_server_ts: originServerTimestamp # Instead of originServerTs + start: begin # Because start() is a method in BaseJob + m.upload.size: uploadSize + m.homeserver: homeserver + m.identity_server: identityServer + m.change_password: changePassword + m.room_versions: roomVersions + AuthenticationData/additionalProperties: authInfo + + # Structure inside `types`: + # - swaggerType: + # OR + # - swaggerType: + # - swaggerFormat: + # - /swaggerFormatRegEx/: + # - //: # default, if the format doesn't mach anything above + # WHERE + # targetTypeSpec = targetType OR + # { type: targetType, imports: , } + # swaggerType can be +set/+on pair; attributes from the map under +set + # are added to each type from the sequence under +on. + types: + - +set: &UseOmittable + useOmittable: + imports: [ '"converters.h"' ] + omittedValue: 'none' # See `none` in converters.h + +on: + - integer: + - int64: qint64 + - int32: qint32 + - //: int + - number: + - float: float + - //: double + - boolean: bool + - string: + - byte: &ByteStream + type: QIODevice* + imports: + - binary: *ByteStream + - +set: { avoidCopy: } + +on: + - date: + type: QDate + initializer: QDate::fromString("{{defaultValue}}") + imports: + - dateTime: + type: QDateTime + initializer: QDateTime::fromString("{{defaultValue}}") + imports: + - //: &QString + type: QString + initializer: QStringLiteral("{{defaultValue}}") + isString: + - file: *ByteStream + - +set: { avoidCopy: } + +on: + - object: &QJsonObject { type: QJsonObject, imports: } + - $ref: + - +set: { moveOnly: } + +on: + - /state_event.yaml$/: + { type: StateEventPtr, imports: '"events/eventloader.h"' } + - /room_event.yaml$/: + { type: RoomEventPtr, imports: '"events/eventloader.h"' } + - /event.yaml$/: + { type: EventPtr, imports: '"events/eventloader.h"' } + - /m\.room\.member$/: pass # This $ref is only used in an array, see below + - //: *UseOmittable # Also apply "avoidCopy" to all other ref'ed types + - schema: # Properties of inline structure definitions + - TurnServerCredentials: *QJsonObject # Because it's used as is + - //: *UseOmittable + - array: + - string: QStringList + - +set: { moveOnly: } + +on: + - /^Notification|Result$/: + type: "std::vector<{{1}}>" + imports: '"events/eventloader.h"' + - /m\.room\.member$/: + type: "EventsArray" + imports: '"events/roommemberevent.h"' + - /state_event.yaml$/: StateEvents + - /room_event.yaml$/: RoomEvents + - /event.yaml$/: Events + - //: { type: "QVector<{{1}}>", imports: } + - map: # `additionalProperties` in OpenAPI + - RoomState: + type: "UnorderedMap" + moveOnly: + imports: '"util.h"' + - /.+/: + type: "QHash" + imports: + - //: + type: QVariantHash + imports: + - variant: # A sequence `type` (multitype) in OpenAPI + - /^string,null|null,string$/: *QString + - //: { type: QVariant, imports: } + + #operations: + +mustache: + constants: + # Syntax elements used by GTAD +# _quote: '"' # Common quote for left and right +# _leftQuote: '"' +# _rightQuote: '"' +# _joinChar: ',' # The character used by {{_join}} - not working yet + _comment: '//' + copyrightName: Kitsune Ral + copyrightEmail: + + partials: + _typeRenderer: "{{#scope}}{{scopeCamelCase}}Job::{{/scope}}{{>name}}" + omittedValue: '{}' # default value to initialize omitted parameters with + initializer: '{{defaultValue}}' + cjoin: '{{#hasMore}}, {{/hasMore}}' + + openOmittable: "{{^required?}}{{#useOmittable}}{{^defaultValue}}Omittable<{{/defaultValue}}{{/useOmittable}}{{/required?}}" + closeOmittable: "{{^required?}}{{#useOmittable}}{{^defaultValue}}>{{/defaultValue}}{{/useOmittable}}{{/required?}}" + + maybeOmittableType: "{{>openOmittable}}{{dataType.name}}{{>closeOmittable}}" + qualifiedMaybeOmittableType: "{{>openOmittable}}{{dataType.qualifiedName}}{{>closeOmittable}}" + + maybeCrefType: "{{#avoidCopy}}const {{/avoidCopy}}{{>maybeOmittableType}}{{#avoidCopy}}&{{/avoidCopy}}{{#moveOnly}}&&{{/moveOnly}}" + qualifiedMaybeCrefType: + "{{#avoidCopy}}const {{/avoidCopy}}{{>qualifiedMaybeOmittableType}}{{#avoidCopy}}&{{/avoidCopy}}{{#moveOnly}}&&{{/moveOnly}}" + + maybeCrefJsonObject: "{{^propertyMap}}const QJsonObject&{{/propertyMap}}{{#propertyMap}}QJsonObject{{/propertyMap}}" + takeOrValue: "{{#propertyMap}}take{{/propertyMap}}{{^propertyMap}}value{{/propertyMap}}" + + initializeDefaultValue: "{{#defaultValue}}{{>initializer}}{{/defaultValue}}{{^defaultValue}}{{>omittedValue}}{{/defaultValue}}" + joinedParamDecl: '{{>maybeCrefType}} {{paramName}}{{^required?}} = {{>initializeDefaultValue}}{{/required?}}{{>cjoin}}' + joinedParamDef: '{{>maybeCrefType}} {{paramName}}{{>cjoin}}' + passQueryParams: '{{#queryParams}}{{paramName}}{{>cjoin}}{{/queryParams}}' + + # Doc-comment blocks. Comment indent is managed by clang-format + # (without clang-format there'd have to be a separate partial definition + # for each indent...) + + # For structures that are not supposed to have a summary (e.g., JSON schema) + docCommentShort: |- + {{#description}} + /// {{_}}{{/description}} + docCommentSummary: |- + {{#summary}} \brief {{summary}} + *{{/summary}} + + templates: + data: + .h: "{{>template.h.mustache}}" + .cpp: "{{>template.cpp.mustache}}" + api: + .h: "{{>template.h.mustache}}" + .cpp: "{{>template.cpp.mustache}}" + + #outFilesList: apifiles.txt + diff --git a/gtad/preamble.mustache b/gtad/preamble.mustache new file mode 100644 index 00000000..3ba87d61 --- /dev/null +++ b/gtad/preamble.mustache @@ -0,0 +1,3 @@ +/****************************************************************************** + * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN + */ diff --git a/gtad/template.cpp.mustache b/gtad/template.cpp.mustache new file mode 100644 index 00000000..b3bd4de9 --- /dev/null +++ b/gtad/template.cpp.mustache @@ -0,0 +1,182 @@ +{{>preamble}} +#include "{{filenameBase}}.h" +{{^models}} +#include "converters.h"{{/models}} +{{#operations}} + {{#producesNonJson?}} +#include + {{/producesNonJson?}} +#include +{{/operations}} + +using namespace Quotient; + +{{#models.model}} + {{#in?}} +void JsonObjectConverter<{{qualifiedName}}>::dumpTo( + QJsonObject& jo, const {{qualifiedName}}& pod) +{ {{#propertyMap}} + fillJson(jo, pod.{{nameCamelCase}}); + {{/propertyMap}}{{#parents}} + fillJson<{{name}}>(jo, pod); + {{/parents}}{{#vars}} + addParam<{{^required?}}IfNotEmpty{{/required?}}>(jo, + QStringLiteral("{{baseName}}"), pod.{{nameCamelCase}}); + {{/vars}} +} + {{/in?}} + + {{#out?}} +void JsonObjectConverter<{{qualifiedName}}>::fillFrom( + {{>maybeCrefJsonObject}} jo, {{qualifiedName}}& result) +{ {{#parents}} + fillFromJson<{{qualifiedName}}>(jo, result); + {{/parents}}{{#vars}} + fromJson(jo.{{>takeOrValue}}("{{baseName}}"_ls), result.{{nameCamelCase}}); + {{/vars}}{{#propertyMap}} + fromJson(jo, result.{{nameCamelCase}}); + {{/propertyMap}} +} + {{/out?}} + +{{/models.model}} +{{#operations}} + +static const auto basePath = QStringLiteral("{{basePathWithoutHost}}"); + {{#operation}}{{#models}} + +// Converters +namespace Quotient { + {{#model}} + +template <> struct JsonObjectConverter<{{qualifiedName}}> { + {{#in?}} + static void dumpTo(QJsonObject& jo, const {{qualifiedName}}& pod) + { {{#propertyMap}} + fillJson(jo, pod.{{nameCamelCase}}); + {{/propertyMap}}{{#parents}} + fillJson<{{name}}>(jo, pod); + {{/parents}}{{#vars}} + addParam<{{^required?}}IfNotEmpty{{/required?}}>(jo, + QStringLiteral("{{baseName}}"), pod.{{nameCamelCase}}); + {{/vars}} + } + {{/in?}} + {{#out?}} + static void fillFrom({{>maybeCrefJsonObject}} jo, {{qualifiedName}}& result) + { {{#parents}} + fillFromJson<{{qualifiedName}}{{!of the parent!}}>(jo, result); + {{/parents}}{{#vars}} + fromJson(jo.{{>takeOrValue}}("{{baseName}}"_ls), + result.{{nameCamelCase}}); + {{/vars}}{{#propertyMap}} + fromJson(jo, result.{{nameCamelCase}}); + {{/propertyMap}} + } + {{/out?}} +}; + {{/model}} + +} // namespace Quotient + {{/models}} + {{#responses}}{{#normalResponse?}}{{#allProperties?}} + +class {{camelCaseOperationId}}Job::Private +{ + public:{{#allProperties}} + {{>maybeOmittableType}} {{paramName}};{{/allProperties}} +}; + {{/allProperties?}}{{/normalResponse?}}{{/responses}} + {{#queryParams?}} + +BaseJob::Query queryTo{{camelCaseOperationId}}( + {{#queryParams}}{{>joinedParamDef}}{{/queryParams}}) +{ + BaseJob::Query _q;{{#queryParams}} + addParam<{{^required?}}IfNotEmpty{{/required?}}>(_q, + QStringLiteral("{{baseName}}"), {{paramName}});{{/queryParams}} + return _q; +} + {{/queryParams?}} + {{^bodyParams}} + +QUrl {{camelCaseOperationId}}Job::makeRequestUrl(QUrl baseUrl{{#allParams?}}, + {{#allParams}}{{>joinedParamDef}}{{/allParams}}{{/allParams?}}) +{ + return BaseJob::makeRequestUrl(std::move(baseUrl), + basePath{{#pathParts}} % {{_}}{{/pathParts}}{{#queryParams?}}, + queryTo{{camelCaseOperationId}}({{>passQueryParams}}){{/queryParams?}}); +} {{/bodyParams}} + +{{camelCaseOperationId}}Job::{{camelCaseOperationId}}Job( + {{#allParams}}{{>joinedParamDef}}{{/allParams}}) + : BaseJob(HttpVerb::{{#_cap}}{{#_tolower}}{{httpMethod}}{{/_tolower}}{{/_cap}}, + QStringLiteral("{{camelCaseOperationId}}Job"), {{!object name}} + basePath{{#pathParts}} % {{_}}{{/pathParts}} {{!API endpoint}} + {{#queryParams? + }} , queryTo{{camelCaseOperationId}}({{>passQueryParams}}) + {{/queryParams? + }}{{#skipAuth}}{{#queryParams?}}, {}{{/queryParams?}}, false{{/skipAuth}} ) + {{#responses}}{{#normalResponse?}}{{#allProperties? + }}, d(new Private){{/allProperties?}}{{/normalResponse?}}{{/responses}} +{ {{#headerParams}} + setRequestHeader("{{baseName}}", {{paramName}}.toLatin1()); + {{/headerParams}}{{#bodyParams?}} + {{#inlineBody}} + setRequestData(Data({{!avoid extra linebreaks}}{{ + #consumesNonJson?}}{{nameCamelCase}}{{/consumesNonJson?}}{{ + ^consumesNonJson?}}toJson({{nameCamelCase}}){{/consumesNonJson? + }})); + {{/inlineBody}}{{^inlineBody}} + QJsonObject _data; + {{#bodyParams}} + addParam<{{^required?}}IfNotEmpty{{/required?}}>(_data, + QStringLiteral("{{baseName}}"), {{paramName}}); + {{/bodyParams}} + setRequestData(_data); + {{/inlineBody}} + {{/bodyParams?}}{{#producesNonJson?}} + setExpectedContentTypes({ {{#produces}}"{{_}}"{{>cjoin}}{{/produces}} }); + {{/producesNonJson?}} +} + {{#responses}}{{#normalResponse?}}{{#allProperties?}} + +{{camelCaseOperationId}}Job::~{{camelCaseOperationId}}Job() = default; + {{#allProperties}} + +{{>qualifiedMaybeCrefType}} + {{camelCaseOperationId}}Job::{{paramName}}(){{^moveOnly}} const{{/moveOnly}} +{ + return {{#moveOnly}}std::move({{/moveOnly}}d->{{paramName}}{{#moveOnly}}){{/moveOnly}}; +} + {{/allProperties}} + + {{#producesNonJson?}} +BaseJob::Status {{camelCaseOperationId}}Job::parseReply(QNetworkReply* reply) +{ + {{! We don't check for required headers yet }} + {{#headers}}d->{{paramName}} = reply->rawHeader("{{baseName}}"); + {{/headers}}{{#properties}}d->{{paramName}} = reply;{{/properties}} + return Success; +} + {{/producesNonJson?}}{{^producesNonJson?}} +BaseJob::Status {{camelCaseOperationId}}Job::parseJson(const QJsonDocument& data) +{ + {{#inlineResponse}} + fromJson(data, d->{{paramName}}); + {{/inlineResponse}}{{^inlineResponse}} + auto json = data.object(); + {{#properties}}{{#required?}} + if (!json.contains("{{baseName}}"_ls)) + return { IncorrectResponse, + "The key '{{baseName}}' not found in the response" }; + {{/required?}} + fromJson(json.value("{{baseName}}"_ls), d->{{paramName}}); + {{/properties}} + {{/inlineResponse}} + + return Success; +} + {{/producesNonJson?}} + {{/allProperties?}}{{/normalResponse?}}{{/responses}} +{{/operation}}{{/operations}} diff --git a/gtad/template.h.mustache b/gtad/template.h.mustache new file mode 100644 index 00000000..404aafe8 --- /dev/null +++ b/gtad/template.h.mustache @@ -0,0 +1,119 @@ +{{>preamble}} +#pragma once + +{{#operations}} +#include "jobs/basejob.h"{{/operations}} +{{#models}} +#include "converters.h"{{/models}} +{{#imports}} +#include {{_}}{{/imports}} + +namespace Quotient { +{{#models}} + +// Data structures + + {{#model}} +{{>docCommentShort}} +struct {{name}}{{#parents?}} : {{#parents}}{{name}}{{>cjoin}}{{/parents}}{{/parents?}} +{ {{#vars}} + + {{>docCommentShort}} + {{>maybeOmittableType}} {{nameCamelCase}}; + {{/vars}}{{#propertyMap}} + + {{>docCommentShort}} + {{>maybeOmittableType}} {{nameCamelCase}}; + {{/propertyMap}} +}; + +template <> struct JsonObjectConverter<{{name}}> +{ + {{#in?}} + static void dumpTo(QJsonObject& jo, const {{name}}& pod); + {{/in?}} + {{#out?}} + static void fillFrom({{>maybeCrefJsonObject}} jo, {{name}}& pod); + {{/out?}} +}; + {{/model}} +{{/models}} +{{#operations}} + +// Operations + {{#operation}} + +/*!{{>docCommentSummary}}{{#description}} + * {{_}}{{/description}} + */ +class {{camelCaseOperationId}}Job : public BaseJob { +public: {{#models}} + // Inner data structures + {{#model}} + + {{>docCommentShort}} + struct {{name}}{{#parents?}} : + {{#parents}}{{name}}{{>cjoin}}{{/parents}}{{/parents?}} + { + {{#vars}} + {{>docCommentShort}} + {{>maybeOmittableType}} {{nameCamelCase}}; + {{/vars}} + {{#propertyMap}} + {{>docCommentShort}} + {{>maybeOmittableType}} {{nameCamelCase}}; + {{/propertyMap}} + }; + {{/model}} + + // Construction/destruction + + {{/models}} + {{^allParams?}} + {{#summary}} + /// {{summary}} + {{/summary}} + {{/allParams?}}{{#allParams?}} + /*!{{>docCommentSummary}} + {{#allParams}} + * \param {{nameCamelCase}}{{#description}} + * {{_}}{{/description}} + {{/allParams}} + */ + {{/allParams?}} + explicit {{camelCaseOperationId}}Job({{#allParams}}{{>joinedParamDecl}}{{/allParams}}); + {{^bodyParams}} + + /*! \brief Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for {{camelCaseOperationId}}Job + * is necessary but the job itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl{{#allParams?}}, + {{#allParams}}{{>joinedParamDecl}}{{/allParams}}{{/allParams?}}); + {{/bodyParams}} + {{#responses}}{{#normalResponse?}}{{#allProperties?}} + ~{{camelCaseOperationId}}Job() override; + + // Result properties + {{#allProperties}} + + {{>docCommentShort}} + {{>maybeCrefType}} {{paramName}}(){{^moveOnly}} const{{/moveOnly}}; + {{/allProperties}} + +protected: {{#producesNonJson?}} + Status parseReply(QNetworkReply* reply) override; + {{/producesNonJson?}}{{^producesNonJson?}} + Status parseJson(const QJsonDocument& data) override; + {{/producesNonJson?}} + +private: + class Private; + QScopedPointer d; + {{/allProperties?}}{{/normalResponse?}}{{/responses}} +}; + {{/operation}} +{{/operations}} + +} // namespace Quotient -- cgit v1.2.3 From a0430b1fb722a77ad7cbd28f181727d46d92b3a2 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sat, 6 Jun 2020 20:47:56 +0200 Subject: gtad/*: optimise and use latest GTAD features - The generated code is updated to be compatible with the BaseJob changes introduced in the previous commit. This includes greatly reducing the number of header files that have to be explicitly #included, as basejob.h now #includes converters.h. Also, thanks to the changes in BaseJob, none of generated job classes needs a pimpl Private class. - gtad/template.*.mustache files are replaced with data.h.mustache for data structures (entirely defined in header files from now on) and operation.*.mustache for API operations (also massively moved to header files, possibly also becoming header-only in the future). - New variable-dropping and title-overring features in GTAD 0.7 allow to use the upstream matrix-doc repo to generate the code. - CMakeLists.txt makes use of file globbing with CONFIGURE_DEPENDS where possible to alleviate build reconfiguration after a GTAD call. --- CMakeLists.txt | 51 +++++++------ gtad/data.h.mustache | 53 +++++++++++++ gtad/gtad.yaml | 102 +++++++++++++++++-------- gtad/operation.cpp.mustache | 58 ++++++++++++++ gtad/operation.h.mustache | 128 +++++++++++++++++++++++++++++++ gtad/template.cpp.mustache | 182 -------------------------------------------- gtad/template.h.mustache | 119 ----------------------------- libquotient.pri | 4 - 8 files changed, 336 insertions(+), 361 deletions(-) create mode 100644 gtad/data.h.mustache create mode 100644 gtad/operation.cpp.mustache create mode 100644 gtad/operation.h.mustache delete mode 100644 gtad/template.cpp.mustache delete mode 100644 gtad/template.h.mustache (limited to 'gtad') diff --git a/CMakeLists.txt b/CMakeLists.txt index 1e336673..c61c2682 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -184,21 +184,21 @@ set(lib_SRCS ) set(CSAPI_DIR csapi) +set(FULL_CSAPI_DIR lib/${CSAPI_DIR}) set(ASAPI_DEF_DIR application-service/definitions) set(ISAPI_DEF_DIR identity/definitions) -foreach (D ${CSAPI_DIR} ${CSAPI_DIR}/definitions - ${CSAPI_DIR}/definitions/wellknown ${ASAPI_DEF_DIR} ${ISAPI_DEF_DIR}) - aux_source_directory(lib/${D} api_SRCS) -endforeach() +if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.12.0") + set(add_CONFIGURE_DEPENDS "CONFIGURE_DEPENDS") +endif() +file(GLOB_RECURSE api_SRCS ${add_CONFIGURE_DEPENDS} ${FULL_CSAPI_DIR}/*.cpp) # Make no mistake: CMake cannot run gtad first and then populate the list of -# resulting api_SRCS files. In other words, placing the above foreach after -# the custom targets definition won't bring the desired result: +# resulting api_SRCS files. In other words, placing the above statement after +# the add_custom_target() call below won't bring the desired result: # CMake will execute it at cmake invocation and gtad will only run later # when building the update-api target. If you see that gtad has created -# new files you have to re-run cmake. -# TODO: check `file(GLOB_RECURSE ... CONFIGURE_DEPENDS)` (from CMake 3.14) +# new files you have to re-run cmake. CONFIGURE_DEPENDS somewhat helps that. if (MATRIX_DOC_PATH AND GTAD_PATH) set(FULL_CSAPI_SRC_DIR ${ABS_API_DEF_PATH}/client-server) file(GLOB_RECURSE API_DEFS RELATIVE ${PROJECT_SOURCE_DIR} @@ -206,7 +206,7 @@ if (MATRIX_DOC_PATH AND GTAD_PATH) ${ABS_API_DEF_PATH}/${ASAPI_DEF_DIR}/*.yaml ${ABS_API_DEF_PATH}/${ISAPI_DEF_DIR}/*.yaml ) - add_custom_target(update-api + add_custom_target(generate-unformatted-api ${ABS_GTAD_PATH} --config ../gtad/gtad.yaml --out ${CSAPI_DIR} ${FULL_CSAPI_SRC_DIR} old_sync.yaml- room_initial_sync.yaml- # deprecated @@ -214,25 +214,30 @@ if (MATRIX_DOC_PATH AND GTAD_PATH) sync.yaml- # we have a better handcrafted implementation WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/lib SOURCES gtad/gtad.yaml - gtad/template.h.mustache - gtad/template.cpp.mustache + gtad/data.h.mustache + gtad/operation.h.mustache + gtad/operation.cpp.mustache ${API_DEFS} VERBATIM ) if (ABS_CLANG_FORMAT) - # TODO: list(TRANSFORM) is available from CMake 3.12 - foreach (S ${api_SRCS}) - string (REGEX REPLACE ".cpp$" ".h" H ${S}) - list(APPEND api_HDRS ${H}) - endforeach() set(CLANG_FORMAT_ARGS -i -sort-includes ${CLANG_FORMAT_ARGS}) - add_custom_command(TARGET update-api POST_BUILD - COMMAND ${ABS_CLANG_FORMAT} ${CLANG_FORMAT_ARGS} ${api_SRCS} - COMMAND ${ABS_CLANG_FORMAT} ${CLANG_FORMAT_ARGS} ${api_HDRS} - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} - VERBATIM - COMMENT "Formatting files" - ) + # FIXME: the list of files should be produced after GTAD has run. + # For now it's produced at CMake invocation; and if file() doesn't file + # any files clang-format chokes; also, + file(GLOB_RECURSE api_ALL_SRCS ${add_CONFIGURE_DEPENDS} + ${FULL_CSAPI_DIR}/*.* + lib/${ASAPI_DEF_DIR}/*.* + lib/${ISAPI_DEF_DIR}/*.*) + if (api_ALL_SRCS) + add_custom_target(format-api + ${ABS_CLANG_FORMAT} ${CLANG_FORMAT_ARGS} ${api_ALL_SRCS} + DEPENDS generate-unformatted-api + VERBATIM) + add_custom_target(update-api DEPENDS format-api) + else() + add_custom_target(update-api DEPENDS generate-unformatted-api) + endif() endif() endif() diff --git a/gtad/data.h.mustache b/gtad/data.h.mustache new file mode 100644 index 00000000..32ea85ee --- /dev/null +++ b/gtad/data.h.mustache @@ -0,0 +1,53 @@ +{{>preamble}} +#pragma once + +#include "converters.h" +{{#imports}} +#include {{_}}{{/imports}} + +namespace Quotient { +{{#models}} + {{#model}} +{{>docCommentShort}} +struct {{name}}{{#parents?}} : {{#parents}}{{name}}{{>cjoin}}{{/parents}}{{/parents?}} +{ {{#vars}} + + {{>docCommentShort}} + {{>maybeOmittableType}} {{nameCamelCase}}; + {{/vars}}{{#propertyMap}} + + {{>docCommentShort}} + {{>maybeOmittableType}} {{nameCamelCase}}; + {{/propertyMap}} +}; + +template <> struct JsonObjectConverter<{{name}}> +{ + {{#in?}} + static void dumpTo(QJsonObject& jo, const {{name}}& pod) + { {{#propertyMap}} + fillJson(jo, pod.{{nameCamelCase}}); + {{/propertyMap}}{{#parents}} + fillJson<{{name}}>(jo, pod); + {{/parents}}{{#vars}} + addParam<{{^required?}}IfNotEmpty{{/required?}}>(jo, + QStringLiteral("{{baseName}}"), pod.{{nameCamelCase}}); + {{/vars}} + } + {{/in?}} + {{#out?}} + static void fillFrom({{>maybeCrefJsonObject}} jo, {{name}}& pod) + { {{#parents}} + fillFromJson<{{qualifiedName}}>(jo, pod); + {{/parents}}{{#vars}} + fromJson(jo.{{>takeOrValue}}("{{baseName}}"_ls), pod.{{nameCamelCase}}); + {{/vars}}{{#propertyMap}} + fromJson(jo, pod.{{nameCamelCase}}); + {{/propertyMap}} + } + {{/out?}} +}; + + {{/model}} +{{/models}} +} // namespace Quotient diff --git a/gtad/gtad.yaml b/gtad/gtad.yaml index 51f9e26b..045f5f35 100644 --- a/gtad/gtad.yaml +++ b/gtad/gtad.yaml @@ -15,6 +15,11 @@ analyzer: m.change_password: changePassword m.room_versions: roomVersions AuthenticationData/additionalProperties: authInfo + /\b(Location|Protocol|User)$/: 'ThirdParty$&' + # These parameters are deprecated and unused in Quotient; so drop them + login>/user: "" + login>/medium: "" + login>/address: "" # Structure inside `types`: # - swaggerType: @@ -31,8 +36,7 @@ analyzer: types: - +set: &UseOmittable useOmittable: - imports: [ '"converters.h"' ] - omittedValue: 'none' # See `none` in converters.h + omittedValue: 'none' # Quotient::none in lib/util.h +on: - integer: - int64: qint64 @@ -52,11 +56,9 @@ analyzer: - date: type: QDate initializer: QDate::fromString("{{defaultValue}}") - imports: - dateTime: type: QDateTime initializer: QDateTime::fromString("{{defaultValue}}") - imports: - //: &QString type: QString initializer: QStringLiteral("{{defaultValue}}") @@ -64,7 +66,7 @@ analyzer: - file: *ByteStream - +set: { avoidCopy: } +on: - - object: &QJsonObject { type: QJsonObject, imports: } + - object: &QJsonObject { type: QJsonObject } - $ref: - +set: { moveOnly: } +on: @@ -74,10 +76,16 @@ analyzer: { type: RoomEventPtr, imports: '"events/eventloader.h"' } - /event.yaml$/: { type: EventPtr, imports: '"events/eventloader.h"' } - - /m\.room\.member$/: pass # This $ref is only used in an array, see below + - /m\.room\.member$/: void # Skip resolving; see EventsArray<> below + - '/^(\./)?definitions/request_email_validation.yaml$/': + title: EmailValidationData + - '/^(\./)?definitions/request_msisdn_validation.yaml$/': + title: MsisdnValidationData + - /_filter.yaml$/: # Event/RoomEventFilters do NOT need Omittable<> - //: *UseOmittable # Also apply "avoidCopy" to all other ref'ed types - - schema: # Properties of inline structure definitions - - TurnServerCredentials: *QJsonObject # Because it's used as is + - schema: + - getTurnServer<: *QJsonObject # It's used as an opaque JSON object + - RoomFilter: # A structure inside Filter, same story as with other filters - //: *UseOmittable - array: - string: QStringList @@ -86,27 +94,23 @@ analyzer: - /^Notification|Result$/: type: "std::vector<{{1}}>" imports: '"events/eventloader.h"' - - /m\.room\.member$/: + - /m\.room\.member$/: # Only used in an array (see also above) type: "EventsArray" imports: '"events/roommemberevent.h"' - /state_event.yaml$/: StateEvents - /room_event.yaml$/: RoomEvents - /event.yaml$/: Events - - //: { type: "QVector<{{1}}>", imports: } + - //: "QVector<{{1}}>" - map: # `additionalProperties` in OpenAPI - RoomState: type: "UnorderedMap" moveOnly: - imports: '"util.h"' - /.+/: type: "QHash" - imports: - - //: - type: QVariantHash - imports: + - //: QVariantHash - variant: # A sequence `type` (multitype) in OpenAPI - /^string,null|null,string$/: *QString - - //: { type: QVariant, imports: } + - //: QVariant #operations: @@ -127,43 +131,75 @@ mustache: initializer: '{{defaultValue}}' cjoin: '{{#hasMore}}, {{/hasMore}}' - openOmittable: "{{^required?}}{{#useOmittable}}{{^defaultValue}}Omittable<{{/defaultValue}}{{/useOmittable}}{{/required?}}" - closeOmittable: "{{^required?}}{{#useOmittable}}{{^defaultValue}}>{{/defaultValue}}{{/useOmittable}}{{/required?}}" + openOmittable: + "{{^required?}}{{#useOmittable}}\ + {{^defaultValue}}Omittable<{{/defaultValue}}\ + {{/useOmittable}}{{/required?}}" + closeOmittable: + "{{^required?}}{{#useOmittable}}\ + {{^defaultValue}}>{{/defaultValue}}\ + {{/useOmittable}}{{/required?}}" maybeOmittableType: "{{>openOmittable}}{{dataType.name}}{{>closeOmittable}}" - qualifiedMaybeOmittableType: "{{>openOmittable}}{{dataType.qualifiedName}}{{>closeOmittable}}" + qualifiedMaybeOmittableType: + "{{>openOmittable}}{{dataType.qualifiedName}}{{>closeOmittable}}" - maybeCrefType: "{{#avoidCopy}}const {{/avoidCopy}}{{>maybeOmittableType}}{{#avoidCopy}}&{{/avoidCopy}}{{#moveOnly}}&&{{/moveOnly}}" + ref: "{{#avoidCopy}}&{{/avoidCopy}}{{#moveOnly}}&&{{/moveOnly}}" + maybeCrefType: + "{{#avoidCopy}}const {{/avoidCopy}}{{>maybeOmittableType}}{{>ref}}" qualifiedMaybeCrefType: - "{{#avoidCopy}}const {{/avoidCopy}}{{>qualifiedMaybeOmittableType}}{{#avoidCopy}}&{{/avoidCopy}}{{#moveOnly}}&&{{/moveOnly}}" + "{{#avoidCopy}}const {{/avoidCopy}}{{>qualifiedMaybeOmittableType}}{{>ref}}" - maybeCrefJsonObject: "{{^propertyMap}}const QJsonObject&{{/propertyMap}}{{#propertyMap}}QJsonObject{{/propertyMap}}" - takeOrValue: "{{#propertyMap}}take{{/propertyMap}}{{^propertyMap}}value{{/propertyMap}}" + maybeCrefJsonObject: + "{{^propertyMap}}const QJsonObject&{{/propertyMap}}\ + {{#propertyMap}}QJsonObject{{/propertyMap}}" - initializeDefaultValue: "{{#defaultValue}}{{>initializer}}{{/defaultValue}}{{^defaultValue}}{{>omittedValue}}{{/defaultValue}}" - joinedParamDecl: '{{>maybeCrefType}} {{paramName}}{{^required?}} = {{>initializeDefaultValue}}{{/required?}}{{>cjoin}}' - joinedParamDef: '{{>maybeCrefType}} {{paramName}}{{>cjoin}}' - passQueryParams: '{{#queryParams}}{{paramName}}{{>cjoin}}{{/queryParams}}' + takeOrValue: + "{{#propertyMap}}take{{/propertyMap}}{{^propertyMap}}value{{/propertyMap}}" + takeOrLoad: "{{#moveOnly}}take{{/moveOnly}}{{^moveOnly}}load{{/moveOnly}}" + + initializeDefaultValue: + "{{#defaultValue}}{{>initializer}}{{/defaultValue}}\ + {{^defaultValue}}{{>omittedValue}}{{/defaultValue}}" + + # No inner indents in folded values! + + joinedParamDecl: >- + {{>maybeCrefType}} {{paramName}} + {{^required?}} = {{>initializeDefaultValue}}{{/required?}}{{>cjoin}} + joinedParamDef: "{{>maybeCrefType}} {{paramName}}{{>cjoin}}" + + passPathAndMaybeQuery: >- + QStringLiteral("{{basePathWithoutHost}}") + {{#pathParts}} % {{_}}{{/pathParts}}{{#queryParams?}}, + queryTo{{camelCaseOperationId}}( + {{#queryParams}}{{paramName}}{{>cjoin}}{{/queryParams}}){{/queryParams?}} + + nonInlineResponseSignature: |- + {{>docCommentShort}} + {{>maybeOmittableType}} {{paramName}}(){{^moveOnly}} const{{/moveOnly}} # Doc-comment blocks. Comment indent is managed by clang-format # (without clang-format there'd have to be a separate partial definition - # for each indent...) + # for each indent...) but we take care of line breaks to maintain + # some sanity even before clang-format - # For structures that are not supposed to have a summary (e.g., JSON schema) + # This is for structures that don't expect a summary (e.g., JSON schema) docCommentShort: |- {{#description}} /// {{_}}{{/description}} + # For structures with the summary, a common partial for summary is here; + # the main part is different in different places docCommentSummary: |- {{#summary}} \brief {{summary}} *{{/summary}} templates: data: - .h: "{{>template.h.mustache}}" - .cpp: "{{>template.cpp.mustache}}" + .h: "{{>data.h.mustache}}" api: - .h: "{{>template.h.mustache}}" - .cpp: "{{>template.cpp.mustache}}" + .h: "{{>operation.h.mustache}}" + .cpp: "{{>operation.cpp.mustache}}" #outFilesList: apifiles.txt diff --git a/gtad/operation.cpp.mustache b/gtad/operation.cpp.mustache new file mode 100644 index 00000000..3c3396e9 --- /dev/null +++ b/gtad/operation.cpp.mustache @@ -0,0 +1,58 @@ +{{>preamble}} +#include "{{filenameBase}}.h" + +#include + +using namespace Quotient; +{{#operations}}{{#operation}} + {{#queryParams?}} + +auto queryTo{{camelCaseOperationId}}( + {{#queryParams}}{{>joinedParamDef}}{{/queryParams}}) +{ + BaseJob::Query _q;{{#queryParams}} + addParam<{{^required?}}IfNotEmpty{{/required?}}>(_q, + QStringLiteral("{{baseName}}"), {{paramName}});{{/queryParams}} + return _q; +} + {{/queryParams?}} + {{^hasBody?}} + +QUrl {{camelCaseOperationId}}Job::makeRequestUrl(QUrl baseUrl{{#allParams?}}, + {{#allParams}}{{>joinedParamDef}}{{/allParams}}{{/allParams?}}) +{ + return BaseJob::makeRequestUrl(std::move(baseUrl), {{>passPathAndMaybeQuery}}); +} {{/hasBody?}} + +{{camelCaseOperationId}}Job::{{camelCaseOperationId}}Job( + {{#allParams}}{{>joinedParamDef}}{{/allParams}}) + : BaseJob(HttpVerb::{{#_cap}}{{#_tolower}}{{httpMethod}}{{/_tolower}}{{/_cap}}, + {{!object name}}QStringLiteral("{{camelCaseOperationId}}Job"), + {{>passPathAndMaybeQuery}} + {{#skipAuth}}{{#queryParams?}}, {}{{/queryParams?}}, false{{/skipAuth}} ) +{ {{#headerParams}} + setRequestHeader("{{baseName}}", {{paramName}}.toLatin1()); + {{/headerParams}}{{#inlineBody}}{{^propertyMap}}{{^bodyParams?}} + setRequestData(Data({{#consumesNonJson?}}{{nameCamelCase}}{{/consumesNonJson? + }}{{^consumesNonJson?}}toJson({{nameCamelCase}}){{/consumesNonJson?}})); + {{/bodyParams?}}{{/propertyMap}}{{/inlineBody + }}{{^consumesNonJson?}}{{#bodyParams?}} + QJsonObject _data; + {{#propertyMap}} + fillJson(_data, {{nameCamelCase}}); + {{/propertyMap}}{{#inlineBody}} + fillJson<{{>maybeOmittableType}}>(_data, {{paramName}}); + {{/inlineBody}}{{#bodyParams}} + addParam<{{^required?}}IfNotEmpty{{/required?}}>(_data, + QStringLiteral("{{baseName}}"), {{paramName}}); + {{/bodyParams}} + setRequestData(std::move(_data)); + {{/bodyParams?}}{{/consumesNonJson?}}{{#producesNonJson?}} + setExpectedContentTypes({ {{#produces}}"{{_}}"{{>cjoin}}{{/produces}} }); + {{/producesNonJson?}}{{^producesNonJson? + }}{{#responses}}{{#normalResponse?}}{{#properties}}{{#required?}} + addExpectedKey("{{baseName}}"); + {{/required?}}{{/properties}}{{/normalResponse?}}{{/responses + }}{{/producesNonJson?}} +} +{{/operation}}{{/operations}} diff --git a/gtad/operation.h.mustache b/gtad/operation.h.mustache new file mode 100644 index 00000000..34c8a361 --- /dev/null +++ b/gtad/operation.h.mustache @@ -0,0 +1,128 @@ +{{>preamble}} +#pragma once + +#include "jobs/basejob.h" +{{#imports}} +#include {{_}}{{/imports}} +{{#operations.producesNonJson?}} +#include {{/operations.producesNonJson?}} + +namespace Quotient { +{{#operations.operation}} + +/*!{{>docCommentSummary}}{{#description}} + * {{_}}{{/description}} + */ +class {{camelCaseOperationId}}Job : public BaseJob { +public: + {{#models}} + // Inner data structures + {{#model}} + + {{>docCommentShort}} + struct {{name}}{{#parents?}} : + {{!Quotient:: is a workaround for cases when the same name is used for + the outer and the inner structure (unfortunately very easy + to hit if you don't give a title to the structure that has + $ref and other properties)}} + {{#parents}}Quotient::{{name}}{{>cjoin}}{{/parents}}{{/parents?}} + { + {{#vars}} + {{>docCommentShort}} + {{>maybeOmittableType}} {{nameCamelCase}}; + {{/vars}} + {{#propertyMap}} + {{>docCommentShort}} + {{>maybeOmittableType}} {{nameCamelCase}}; + {{/propertyMap}} + }; + {{/model}} + + // Construction/destruction + + {{/models}} + {{#allParams?}} + /*!{{>docCommentSummary}} + {{#allParams}} + * + * \param {{nameCamelCase}}{{#description}} + * {{_}}{{/description}} + {{/allParams}} + */ + {{/allParams?}}{{^allParams?}} + {{#summary}} + /// {{summary}} + {{/summary}} + {{/allParams?}} + explicit {{camelCaseOperationId}}Job({{#allParams}}{{>joinedParamDecl}}{{/allParams}}); + {{^hasBody?}} + + /*! \brief Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for {{camelCaseOperationId}}Job + * is necessary but the job itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl{{#allParams?}}, + {{#allParams}}{{>joinedParamDecl}}{{/allParams}}{{/allParams?}}); + {{/hasBody?}} + {{#responses}}{{#normalResponse?}}{{#allProperties?}} + + // Result properties + {{#headers}} + + {{>nonInlineResponseSignature}} + { + return reply()->rawHeader("{{baseName}}"); + } + {{/headers}}{{#inlineResponse}} + + {{>docCommentShort}} + {{dataType.name}} {{paramName}}(){{^moveOnly}}{{^producesNonJson?}} const{{/producesNonJson?}}{{/moveOnly}} + { + return {{#producesNonJson?}}reply(){{/producesNonJson?}} + {{^producesNonJson? + }}fromJson<{{dataType.name}}>(jsonData()){{/producesNonJson? + }}; + } + {{/inlineResponse}}{{#properties}} + + {{!there's nothing in #properties if the response is inline}} + {{>nonInlineResponseSignature}} + { + return {{>takeOrLoad}}FromJson<{{>maybeOmittableType}}>("{{baseName}}"_ls); + } + {{/properties}} + {{/allProperties?}}{{/normalResponse?}}{{/responses}} +}; + {{#models.model}} + +template <> struct JsonObjectConverter<{{qualifiedName}}> { + {{#in?}} + static void dumpTo(QJsonObject& jo, const {{qualifiedName}}& pod) + { {{#propertyMap}} + fillJson(jo, pod.{{nameCamelCase}}); + {{/propertyMap}}{{#parents}} + fillJson<{{name}}>(jo, pod); + {{/parents}}{{#vars}} + addParam<{{^required?}}IfNotEmpty{{/required?}}>(jo, + QStringLiteral("{{baseName}}"), pod.{{nameCamelCase}}); + {{/vars}} + } + {{/in?}} + {{#out?}} + static void fillFrom({{>maybeCrefJsonObject}} jo, {{qualifiedName}}& result) + { {{#parents}} + fillFromJson<{{name}}{{!of the parent!}}>(jo, result); + {{/parents}}{{#vars}} + fromJson(jo.{{>takeOrValue}}("{{baseName}}"_ls), + result.{{nameCamelCase}}); + {{/vars}}{{#propertyMap}} + fromJson(jo, result.{{nameCamelCase}}); + {{/propertyMap}} + } + {{/out?}} +}; + {{/models.model}} +{{/operations.operation}} + +} // namespace Quotient diff --git a/gtad/template.cpp.mustache b/gtad/template.cpp.mustache deleted file mode 100644 index b3bd4de9..00000000 --- a/gtad/template.cpp.mustache +++ /dev/null @@ -1,182 +0,0 @@ -{{>preamble}} -#include "{{filenameBase}}.h" -{{^models}} -#include "converters.h"{{/models}} -{{#operations}} - {{#producesNonJson?}} -#include - {{/producesNonJson?}} -#include -{{/operations}} - -using namespace Quotient; - -{{#models.model}} - {{#in?}} -void JsonObjectConverter<{{qualifiedName}}>::dumpTo( - QJsonObject& jo, const {{qualifiedName}}& pod) -{ {{#propertyMap}} - fillJson(jo, pod.{{nameCamelCase}}); - {{/propertyMap}}{{#parents}} - fillJson<{{name}}>(jo, pod); - {{/parents}}{{#vars}} - addParam<{{^required?}}IfNotEmpty{{/required?}}>(jo, - QStringLiteral("{{baseName}}"), pod.{{nameCamelCase}}); - {{/vars}} -} - {{/in?}} - - {{#out?}} -void JsonObjectConverter<{{qualifiedName}}>::fillFrom( - {{>maybeCrefJsonObject}} jo, {{qualifiedName}}& result) -{ {{#parents}} - fillFromJson<{{qualifiedName}}>(jo, result); - {{/parents}}{{#vars}} - fromJson(jo.{{>takeOrValue}}("{{baseName}}"_ls), result.{{nameCamelCase}}); - {{/vars}}{{#propertyMap}} - fromJson(jo, result.{{nameCamelCase}}); - {{/propertyMap}} -} - {{/out?}} - -{{/models.model}} -{{#operations}} - -static const auto basePath = QStringLiteral("{{basePathWithoutHost}}"); - {{#operation}}{{#models}} - -// Converters -namespace Quotient { - {{#model}} - -template <> struct JsonObjectConverter<{{qualifiedName}}> { - {{#in?}} - static void dumpTo(QJsonObject& jo, const {{qualifiedName}}& pod) - { {{#propertyMap}} - fillJson(jo, pod.{{nameCamelCase}}); - {{/propertyMap}}{{#parents}} - fillJson<{{name}}>(jo, pod); - {{/parents}}{{#vars}} - addParam<{{^required?}}IfNotEmpty{{/required?}}>(jo, - QStringLiteral("{{baseName}}"), pod.{{nameCamelCase}}); - {{/vars}} - } - {{/in?}} - {{#out?}} - static void fillFrom({{>maybeCrefJsonObject}} jo, {{qualifiedName}}& result) - { {{#parents}} - fillFromJson<{{qualifiedName}}{{!of the parent!}}>(jo, result); - {{/parents}}{{#vars}} - fromJson(jo.{{>takeOrValue}}("{{baseName}}"_ls), - result.{{nameCamelCase}}); - {{/vars}}{{#propertyMap}} - fromJson(jo, result.{{nameCamelCase}}); - {{/propertyMap}} - } - {{/out?}} -}; - {{/model}} - -} // namespace Quotient - {{/models}} - {{#responses}}{{#normalResponse?}}{{#allProperties?}} - -class {{camelCaseOperationId}}Job::Private -{ - public:{{#allProperties}} - {{>maybeOmittableType}} {{paramName}};{{/allProperties}} -}; - {{/allProperties?}}{{/normalResponse?}}{{/responses}} - {{#queryParams?}} - -BaseJob::Query queryTo{{camelCaseOperationId}}( - {{#queryParams}}{{>joinedParamDef}}{{/queryParams}}) -{ - BaseJob::Query _q;{{#queryParams}} - addParam<{{^required?}}IfNotEmpty{{/required?}}>(_q, - QStringLiteral("{{baseName}}"), {{paramName}});{{/queryParams}} - return _q; -} - {{/queryParams?}} - {{^bodyParams}} - -QUrl {{camelCaseOperationId}}Job::makeRequestUrl(QUrl baseUrl{{#allParams?}}, - {{#allParams}}{{>joinedParamDef}}{{/allParams}}{{/allParams?}}) -{ - return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath{{#pathParts}} % {{_}}{{/pathParts}}{{#queryParams?}}, - queryTo{{camelCaseOperationId}}({{>passQueryParams}}){{/queryParams?}}); -} {{/bodyParams}} - -{{camelCaseOperationId}}Job::{{camelCaseOperationId}}Job( - {{#allParams}}{{>joinedParamDef}}{{/allParams}}) - : BaseJob(HttpVerb::{{#_cap}}{{#_tolower}}{{httpMethod}}{{/_tolower}}{{/_cap}}, - QStringLiteral("{{camelCaseOperationId}}Job"), {{!object name}} - basePath{{#pathParts}} % {{_}}{{/pathParts}} {{!API endpoint}} - {{#queryParams? - }} , queryTo{{camelCaseOperationId}}({{>passQueryParams}}) - {{/queryParams? - }}{{#skipAuth}}{{#queryParams?}}, {}{{/queryParams?}}, false{{/skipAuth}} ) - {{#responses}}{{#normalResponse?}}{{#allProperties? - }}, d(new Private){{/allProperties?}}{{/normalResponse?}}{{/responses}} -{ {{#headerParams}} - setRequestHeader("{{baseName}}", {{paramName}}.toLatin1()); - {{/headerParams}}{{#bodyParams?}} - {{#inlineBody}} - setRequestData(Data({{!avoid extra linebreaks}}{{ - #consumesNonJson?}}{{nameCamelCase}}{{/consumesNonJson?}}{{ - ^consumesNonJson?}}toJson({{nameCamelCase}}){{/consumesNonJson? - }})); - {{/inlineBody}}{{^inlineBody}} - QJsonObject _data; - {{#bodyParams}} - addParam<{{^required?}}IfNotEmpty{{/required?}}>(_data, - QStringLiteral("{{baseName}}"), {{paramName}}); - {{/bodyParams}} - setRequestData(_data); - {{/inlineBody}} - {{/bodyParams?}}{{#producesNonJson?}} - setExpectedContentTypes({ {{#produces}}"{{_}}"{{>cjoin}}{{/produces}} }); - {{/producesNonJson?}} -} - {{#responses}}{{#normalResponse?}}{{#allProperties?}} - -{{camelCaseOperationId}}Job::~{{camelCaseOperationId}}Job() = default; - {{#allProperties}} - -{{>qualifiedMaybeCrefType}} - {{camelCaseOperationId}}Job::{{paramName}}(){{^moveOnly}} const{{/moveOnly}} -{ - return {{#moveOnly}}std::move({{/moveOnly}}d->{{paramName}}{{#moveOnly}}){{/moveOnly}}; -} - {{/allProperties}} - - {{#producesNonJson?}} -BaseJob::Status {{camelCaseOperationId}}Job::parseReply(QNetworkReply* reply) -{ - {{! We don't check for required headers yet }} - {{#headers}}d->{{paramName}} = reply->rawHeader("{{baseName}}"); - {{/headers}}{{#properties}}d->{{paramName}} = reply;{{/properties}} - return Success; -} - {{/producesNonJson?}}{{^producesNonJson?}} -BaseJob::Status {{camelCaseOperationId}}Job::parseJson(const QJsonDocument& data) -{ - {{#inlineResponse}} - fromJson(data, d->{{paramName}}); - {{/inlineResponse}}{{^inlineResponse}} - auto json = data.object(); - {{#properties}}{{#required?}} - if (!json.contains("{{baseName}}"_ls)) - return { IncorrectResponse, - "The key '{{baseName}}' not found in the response" }; - {{/required?}} - fromJson(json.value("{{baseName}}"_ls), d->{{paramName}}); - {{/properties}} - {{/inlineResponse}} - - return Success; -} - {{/producesNonJson?}} - {{/allProperties?}}{{/normalResponse?}}{{/responses}} -{{/operation}}{{/operations}} diff --git a/gtad/template.h.mustache b/gtad/template.h.mustache deleted file mode 100644 index 404aafe8..00000000 --- a/gtad/template.h.mustache +++ /dev/null @@ -1,119 +0,0 @@ -{{>preamble}} -#pragma once - -{{#operations}} -#include "jobs/basejob.h"{{/operations}} -{{#models}} -#include "converters.h"{{/models}} -{{#imports}} -#include {{_}}{{/imports}} - -namespace Quotient { -{{#models}} - -// Data structures - - {{#model}} -{{>docCommentShort}} -struct {{name}}{{#parents?}} : {{#parents}}{{name}}{{>cjoin}}{{/parents}}{{/parents?}} -{ {{#vars}} - - {{>docCommentShort}} - {{>maybeOmittableType}} {{nameCamelCase}}; - {{/vars}}{{#propertyMap}} - - {{>docCommentShort}} - {{>maybeOmittableType}} {{nameCamelCase}}; - {{/propertyMap}} -}; - -template <> struct JsonObjectConverter<{{name}}> -{ - {{#in?}} - static void dumpTo(QJsonObject& jo, const {{name}}& pod); - {{/in?}} - {{#out?}} - static void fillFrom({{>maybeCrefJsonObject}} jo, {{name}}& pod); - {{/out?}} -}; - {{/model}} -{{/models}} -{{#operations}} - -// Operations - {{#operation}} - -/*!{{>docCommentSummary}}{{#description}} - * {{_}}{{/description}} - */ -class {{camelCaseOperationId}}Job : public BaseJob { -public: {{#models}} - // Inner data structures - {{#model}} - - {{>docCommentShort}} - struct {{name}}{{#parents?}} : - {{#parents}}{{name}}{{>cjoin}}{{/parents}}{{/parents?}} - { - {{#vars}} - {{>docCommentShort}} - {{>maybeOmittableType}} {{nameCamelCase}}; - {{/vars}} - {{#propertyMap}} - {{>docCommentShort}} - {{>maybeOmittableType}} {{nameCamelCase}}; - {{/propertyMap}} - }; - {{/model}} - - // Construction/destruction - - {{/models}} - {{^allParams?}} - {{#summary}} - /// {{summary}} - {{/summary}} - {{/allParams?}}{{#allParams?}} - /*!{{>docCommentSummary}} - {{#allParams}} - * \param {{nameCamelCase}}{{#description}} - * {{_}}{{/description}} - {{/allParams}} - */ - {{/allParams?}} - explicit {{camelCaseOperationId}}Job({{#allParams}}{{>joinedParamDecl}}{{/allParams}}); - {{^bodyParams}} - - /*! \brief Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for {{camelCaseOperationId}}Job - * is necessary but the job itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl{{#allParams?}}, - {{#allParams}}{{>joinedParamDecl}}{{/allParams}}{{/allParams?}}); - {{/bodyParams}} - {{#responses}}{{#normalResponse?}}{{#allProperties?}} - ~{{camelCaseOperationId}}Job() override; - - // Result properties - {{#allProperties}} - - {{>docCommentShort}} - {{>maybeCrefType}} {{paramName}}(){{^moveOnly}} const{{/moveOnly}}; - {{/allProperties}} - -protected: {{#producesNonJson?}} - Status parseReply(QNetworkReply* reply) override; - {{/producesNonJson?}}{{^producesNonJson?}} - Status parseJson(const QJsonDocument& data) override; - {{/producesNonJson?}} - -private: - class Private; - QScopedPointer d; - {{/allProperties?}}{{/normalResponse?}}{{/responses}} -}; - {{/operation}} -{{/operations}} - -} // namespace Quotient diff --git a/libquotient.pri b/libquotient.pri index df58d35b..4fee6723 100644 --- a/libquotient.pri +++ b/libquotient.pri @@ -112,10 +112,6 @@ SOURCES += \ $$SRCPATH/jobs/mediathumbnailjob.cpp \ $$SRCPATH/jobs/downloadfilejob.cpp \ $$files($$SRCPATH/csapi/*.cpp, false) \ - $$files($$SRCPATH/csapi/definitions/*.cpp, false) \ - $$files($$SRCPATH/csapi/definitions/wellknown/*.cpp, false) \ - $$files($$SRCPATH/application-service/definitions/*.cpp, false) \ - $$files($$SRCPATH/identity/definitions/*.cpp, false) \ $$SRCPATH/logging.cpp \ $$SRCPATH/converters.cpp \ $$SRCPATH/settings.cpp \ -- cgit v1.2.3 From 767b97dd885bcc04aac022763e5011115769196a Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 18 Jun 2020 07:31:44 +0200 Subject: gtad: restrict the identifier regex --- gtad/gtad.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'gtad') diff --git a/gtad/gtad.yaml b/gtad/gtad.yaml index 045f5f35..0197e870 100644 --- a/gtad/gtad.yaml +++ b/gtad/gtad.yaml @@ -15,7 +15,7 @@ analyzer: m.change_password: changePassword m.room_versions: roomVersions AuthenticationData/additionalProperties: authInfo - /\b(Location|Protocol|User)$/: 'ThirdParty$&' + /^/(Location|Protocol|User)$/: 'ThirdParty$1' # These parameters are deprecated and unused in Quotient; so drop them login>/user: "" login>/medium: "" -- cgit v1.2.3 From e0ac446f41fdb1c476196a5ebfb72c995a959b8d Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 18 Jun 2020 11:22:02 +0200 Subject: *.h.mustache: minor fixes The Quotient:: workaround seems no more necessary. --- gtad/operation.h.mustache | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'gtad') diff --git a/gtad/operation.h.mustache b/gtad/operation.h.mustache index 34c8a361..36963b9a 100644 --- a/gtad/operation.h.mustache +++ b/gtad/operation.h.mustache @@ -21,11 +21,7 @@ public: {{>docCommentShort}} struct {{name}}{{#parents?}} : - {{!Quotient:: is a workaround for cases when the same name is used for - the outer and the inner structure (unfortunately very easy - to hit if you don't give a title to the structure that has - $ref and other properties)}} - {{#parents}}Quotient::{{name}}{{>cjoin}}{{/parents}}{{/parents?}} + {{#parents}}{{name}}{{>cjoin}}{{/parents}}{{/parents?}} { {{#vars}} {{>docCommentShort}} @@ -44,9 +40,9 @@ public: {{#allParams?}} /*!{{>docCommentSummary}} {{#allParams}} - * * \param {{nameCamelCase}}{{#description}} - * {{_}}{{/description}} + * {{_}}{{/description}}{{#_join}} + * {{/_join}} {{/allParams}} */ {{/allParams?}}{{^allParams?}} @@ -77,7 +73,8 @@ public: {{/headers}}{{#inlineResponse}} {{>docCommentShort}} - {{dataType.name}} {{paramName}}(){{^moveOnly}}{{^producesNonJson?}} const{{/producesNonJson?}}{{/moveOnly}} + {{dataType.name}} {{paramName}}() + {{^moveOnly}}{{^producesNonJson?}} const{{/producesNonJson?}}{{/moveOnly}} { return {{#producesNonJson?}}reply(){{/producesNonJson?}} {{^producesNonJson? -- cgit v1.2.3 From cbd107e595bbb78ef3411a4a92f66d495c6fc5b4 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 18 Jun 2020 22:20:00 +0200 Subject: Rename a few 'data' responses --- gtad/gtad.yaml | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) (limited to 'gtad') diff --git a/gtad/gtad.yaml b/gtad/gtad.yaml index 0197e870..adf5024a 100644 --- a/gtad/gtad.yaml +++ b/gtad/gtad.yaml @@ -16,6 +16,15 @@ analyzer: m.room_versions: roomVersions AuthenticationData/additionalProperties: authInfo /^/(Location|Protocol|User)$/: 'ThirdParty$1' + # Change some response names + /requestTokenTo.*/user: "" login>/medium: "" @@ -71,21 +80,27 @@ analyzer: - +set: { moveOnly: } +on: - /state_event.yaml$/: - { type: StateEventPtr, imports: '"events/eventloader.h"' } + { type: StateEventPtr, imports: "events/eventloader.h" } - /room_event.yaml$/: - { type: RoomEventPtr, imports: '"events/eventloader.h"' } + { type: RoomEventPtr, imports: "events/eventloader.h" } - /event.yaml$/: - { type: EventPtr, imports: '"events/eventloader.h"' } + { type: EventPtr, imports: "events/eventloader.h" } - /m\.room\.member$/: void # Skip resolving; see EventsArray<> below - '/^(\./)?definitions/request_email_validation.yaml$/': title: EmailValidationData - '/^(\./)?definitions/request_msisdn_validation.yaml$/': title: MsisdnValidationData - /_filter.yaml$/: # Event/RoomEventFilters do NOT need Omittable<> + - /public_rooms_response.yaml$/: { _inline: true } - //: *UseOmittable # Also apply "avoidCopy" to all other ref'ed types - schema: - getTurnServer<: *QJsonObject # It's used as an opaque JSON object - - RoomFilter: # A structure inside Filter, same story as with other filters + - PublicRoomResponse: { _inline: true } +# - defineFilter>: &Filter # Force folding into a structure +# type: Filter +# imports: "csapi/definitions/sync_filter.h" +# - getFilter<: *Filter + - RoomFilter: # A structure inside Filter, same story as with *_filter.yaml - //: *UseOmittable - array: - string: QStringList @@ -93,10 +108,10 @@ analyzer: +on: - /^Notification|Result$/: type: "std::vector<{{1}}>" - imports: '"events/eventloader.h"' + imports: "events/eventloader.h" - /m\.room\.member$/: # Only used in an array (see also above) type: "EventsArray" - imports: '"events/roommemberevent.h"' + imports: "events/roommemberevent.h" - /state_event.yaml$/: StateEvents - /room_event.yaml$/: RoomEvents - /event.yaml$/: Events @@ -105,8 +120,7 @@ analyzer: - RoomState: type: "UnorderedMap" moveOnly: - - /.+/: - type: "QHash" + - /.+/: "QHash" - //: QVariantHash - variant: # A sequence `type` (multitype) in OpenAPI - /^string,null|null,string$/: *QString -- cgit v1.2.3