diff --git a/.gitignore b/.gitignore index b694934..7cd6f5d 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -.venv \ No newline at end of file +.venv +__pycache__/ \ No newline at end of file diff --git a/src/config/header.yaml b/src/config/component/response_header.yaml similarity index 100% rename from src/config/header.yaml rename to src/config/component/response_header.yaml diff --git a/src/config/component/security_schemes.yaml b/src/config/component/security_schemes.yaml new file mode 100644 index 0000000..8c7a518 --- /dev/null +++ b/src/config/component/security_schemes.yaml @@ -0,0 +1,61 @@ +openapi: 3.0.3 +info: + title: Twitter OpenAPI + version: 0.0.1 +paths: + /parameters: + get: + parameters: + - name: user-agent + in: header + required: true + schema: + type: string + default: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36" + description: "UserAgent, some APIs may be rejected if changed." + - name: authorization + in: header + required: true + schema: + type: string + default: "Bearer AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA" + description: "It is a constant value and does not need to be changed." + - name: x-twitter-active-user + in: header + required: true + schema: + type: string + default: "yes" + description: "Unknown what this value means." + - name: x-twitter-client-language + in: header + required: true + schema: + type: string + default: "en" + description: "language code." + responses: + "200": + description: "" + +components: + securitySchemes: + UserAgent: + description: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36" + in: header + name: user-agent + type: apiKey + BearerAuth: + description: AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA + scheme: bearer + type: http + ActiveUser: + description: "yes" + in: header + name: x-twitter-active-user + type: apiKey + ClientLanguage: + description: en + in: header + name: x-twitter-client-language + type: apiKey diff --git a/src/config/parameters.yaml b/src/config/parameters.yaml deleted file mode 100644 index c59eea3..0000000 --- a/src/config/parameters.yaml +++ /dev/null @@ -1,155 +0,0 @@ -openapi: 3.0.3 -info: - title: Twitter OpenAPI - version: 0.0.1 -paths: - /parameters: - get: - parameters: - - name: queryId - in: query - required: true - schema: - type: string - default: "{{Query}}" - example: "{{Query}}" - - # {% if get_parameters == string %} - - - name: variables - in: query - required: true - schema: - type: string - default: "{{Variables}}" - example: "{{Variables}}" - - name: features - in: query - required: true - schema: - type: string - default: "{{Features}}" - example: "{{Features}}" - - # {% endif %} - - # {% if get_parameters == object %} - - - name: variables - in: query - required: true - schema: - type: object - - name: features - in: query - required: true - schema: - type: object - - name: queryId - in: query - required: true - schema: - type: object - - # {% endif %} - - # {% if header == parameters %} - - - name: authorization - in: header - required: true - schema: - type: string - default: "Bearer AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA" - description: "It is a constant value and does not need to be changed." - - name: x-twitter-active-user - in: header - required: true - schema: - type: string - default: "yes" - description: "Unknown what this value means." - - name: x-twitter-client-language - in: header - required: true - schema: - type: string - default: "en" - description: "language code." - - name: user-agent - in: header - required: true - schema: - type: string - default: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36" - description: "UserAgent, some APIs may be rejected if changed." - - # {% endif %} - responses: - "200": - description: "" - - post: - # {% if header == parameters %} - - parameters: - - name: authorization - in: header - required: true - schema: - type: string - default: "Bearer AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA" - description: "It is a constant value and does not need to be changed." - - name: x-twitter-active-user - in: header - required: true - schema: - type: string - default: "yes" - description: "Unknown what this value means." - - name: x-twitter-client-language - in: header - required: true - schema: - type: string - default: "en" - description: "language code." - - name: user-agent - in: header - required: true - schema: - type: string - default: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36" - description: "UserAgent, some APIs may be rejected if changed." - - # {% endif %} - - # {% if post_parameters == object %} - - requestBody: - required: true - content: - application/json: - schema: - required: - - "queryId" - - "variables" - - "features" - properties: - queryId: - type: string - default: "{{Query}}" - example: "{{Query}}" - variables: - type: object - # default: "{{Variables}}" - example: "{{Variables}}" - features: - type: object - # default: "{{Features}}" - example: "{{Features}}" - - # {% endif %} - responses: - "200": - description: "" diff --git a/src/config/placeholder.json b/src/config/placeholder.json index 8d3c7aa..072d3c7 100644 --- a/src/config/placeholder.json +++ b/src/config/placeholder.json @@ -1,14 +1,14 @@ { "HomeTimeline": { - "Query": "HCosKfLNW1AcOo3la3mMgg", - "Variables": { + "queryId": "HCosKfLNW1AcOo3la3mMgg", + "variables": { "count": 20, "includePromotedContent": true, "latestControlAvailable": true, "requestContext": "launch", "withCommunity": true }, - "Features": { + "features": { "blue_business_profile_image_shape_enabled": true, "responsive_web_graphql_exclude_directive_enabled": true, "verified_phone_label_enabled": false, @@ -31,14 +31,14 @@ } }, "HomeLatestTimeline": { - "Query": "zhX91JE87mWvfprhYE97xA", - "Variables": { + "queryId": "zhX91JE87mWvfprhYE97xA", + "variables": { "count": 20, "includePromotedContent": true, "latestControlAvailable": true, "requestContext": "launch" }, - "Features": { + "features": { "blue_business_profile_image_shape_enabled": true, "responsive_web_graphql_exclude_directive_enabled": true, "verified_phone_label_enabled": false, @@ -61,12 +61,12 @@ } }, "ListLatestTweetsTimeline": { - "Query": "2TemLyqrMpTeAmysdbnVqw", - "Variables": { + "queryId": "2TemLyqrMpTeAmysdbnVqw", + "variables": { "listId": "53044119", "count": 20 }, - "Features": { + "features": { "blue_business_profile_image_shape_enabled": true, "responsive_web_graphql_exclude_directive_enabled": true, "verified_phone_label_enabled": false, @@ -89,12 +89,12 @@ } }, "UserByScreenName": { - "Query": "sLVLhk0bGj3MVFEKTdax1w", - "Variables": { + "queryId": "sLVLhk0bGj3MVFEKTdax1w", + "variables": { "screen_name": "elonmusk", "withSafetyModeUserFields": true }, - "Features": { + "features": { "blue_business_profile_image_shape_enabled": true, "responsive_web_graphql_exclude_directive_enabled": true, "verified_phone_label_enabled": false, @@ -103,15 +103,15 @@ } }, "ProfileSpotlightsQuery": { - "Query": "9zwVLJ48lmVUk8u_Gh9DmA", - "Variables": { + "queryId": "9zwVLJ48lmVUk8u_Gh9DmA", + "variables": { "screen_name": "elonmusk" }, - "Features": {} + "features": {} }, "UserTweets": { - "Query": "HuTx74BxAnezK1gWvYY7zg", - "Variables": { + "queryId": "HuTx74BxAnezK1gWvYY7zg", + "variables": { "userId": "44196397", "count": 40, "includePromotedContent": true, @@ -119,7 +119,7 @@ "withVoice": true, "withV2Timeline": true }, - "Features": { + "features": { "blue_business_profile_image_shape_enabled": true, "responsive_web_graphql_exclude_directive_enabled": true, "verified_phone_label_enabled": false, @@ -142,8 +142,8 @@ } }, "UserTweetsAndReplies": { - "Query": "RIWc55YCNyUJ-U3HHGYkdg", - "Variables": { + "queryId": "RIWc55YCNyUJ-U3HHGYkdg", + "variables": { "userId": "44196397", "count": 40, "includePromotedContent": true, @@ -151,7 +151,7 @@ "withVoice": true, "withV2Timeline": true }, - "Features": { + "features": { "blue_business_profile_image_shape_enabled": true, "responsive_web_graphql_exclude_directive_enabled": true, "verified_phone_label_enabled": false, @@ -174,8 +174,8 @@ } }, "UserMedia": { - "Query": "YqiE3JL1KNgf9nSljYdxaA", - "Variables": { + "queryId": "YqiE3JL1KNgf9nSljYdxaA", + "variables": { "userId": "44196397", "count": 40, "includePromotedContent": false, @@ -184,7 +184,7 @@ "withVoice": true, "withV2Timeline": true }, - "Features": { + "features": { "blue_business_profile_image_shape_enabled": true, "responsive_web_graphql_exclude_directive_enabled": true, "verified_phone_label_enabled": false, @@ -207,8 +207,8 @@ } }, "Likes": { - "Query": "5fmEkRT-1AdHqEsbVgehMg", - "Variables": { + "queryId": "5fmEkRT-1AdHqEsbVgehMg", + "variables": { "userId": "44196397", "count": 20, "includePromotedContent": false, @@ -217,7 +217,7 @@ "withVoice": true, "withV2Timeline": true }, - "Features": { + "features": { "blue_business_profile_image_shape_enabled": true, "responsive_web_graphql_exclude_directive_enabled": true, "verified_phone_label_enabled": false, @@ -240,12 +240,12 @@ } }, "Bookmarks": { - "Query": "tmd4ifV8RHltzn8ymGg1aw", - "Variables": { + "queryId": "tmd4ifV8RHltzn8ymGg1aw", + "variables": { "count": 20, "includePromotedContent": true }, - "Features": { + "features": { "graphql_timeline_v2_bookmark_timeline": true, "blue_business_profile_image_shape_enabled": true, "responsive_web_graphql_exclude_directive_enabled": true, @@ -269,8 +269,8 @@ } }, "TweetDetail": { - "Query": "wNNG8DBB8EaXw1lq4vFWGA", - "Variables": { + "queryId": "wNNG8DBB8EaXw1lq4vFWGA", + "variables": { "focalTweetId": "1349129669258448897", "with_rux_injections": false, "includePromotedContent": true, @@ -280,7 +280,7 @@ "withVoice": true, "withV2Timeline": true }, - "Features": { + "features": { "blue_business_profile_image_shape_enabled": true, "responsive_web_graphql_exclude_directive_enabled": true, "verified_phone_label_enabled": false, @@ -303,13 +303,13 @@ } }, "Followers": { - "Query": "djdTXDIk2qhd4OStqlUFeQ", - "Variables": { + "queryId": "djdTXDIk2qhd4OStqlUFeQ", + "variables": { "userId": "44196397", "count": 20, "includePromotedContent": false }, - "Features": { + "features": { "blue_business_profile_image_shape_enabled": true, "responsive_web_graphql_exclude_directive_enabled": true, "verified_phone_label_enabled": false, @@ -332,13 +332,13 @@ } }, "Following": { - "Query": "IWP6Zt14sARO29lJT35bBw", - "Variables": { + "queryId": "IWP6Zt14sARO29lJT35bBw", + "variables": { "userId": "44196397", "count": 20, "includePromotedContent": false }, - "Features": { + "features": { "blue_business_profile_image_shape_enabled": true, "responsive_web_graphql_exclude_directive_enabled": true, "verified_phone_label_enabled": false, @@ -361,32 +361,44 @@ } }, "FavoriteTweet": { - "Query": "lI07N6Otwv1PhnEgXILM7A", - "Variables": { - "tweet_id": "1349129669258448897" + "queryId": "lI07N6Otwv1PhnEgXILM7A", + "Parameters": { + "variables": { + "tweet_id": "1349129669258448897" + }, + "features": {} } }, "UnfavoriteTweet": { - "Query": "ZYKSe-w7KEslx3JhSIk5LA", - "Variables": { - "tweet_id": "1349129669258448897" + "queryId": "ZYKSe-w7KEslx3JhSIk5LA", + "Parameters": { + "variables": { + "tweet_id": "1349129669258448897" + }, + "features": {} } }, "CreateRetweet": { - "Query": "ojPdsZsimiJrUGLR1sjUtA", - "Variables": { - "tweet_id": "1349129669258448897" + "queryId": "ojPdsZsimiJrUGLR1sjUtA", + "Parameters": { + "variables": { + "tweet_id": "1349129669258448897" + }, + "features": {} } }, "DeleteRetweet": { - "Query": "iQtK4dl5hBmXewYZuEOKVw", - "Variables": { - "tweet_id": "1349129669258448897" + "queryId": "iQtK4dl5hBmXewYZuEOKVw", + "Parameters": { + "variables": { + "tweet_id": "1349129669258448897" + }, + "features": {} } }, "CreateTweet": { - "Query": "1RyAhNwby-gzGCRVsMxKbQ", - "Variables": { + "queryId": "1RyAhNwby-gzGCRVsMxKbQ", + "variables": { "tweet_text": "test", "media": { "media_entities": [], @@ -395,7 +407,7 @@ "semantic_annotation_ids": [], "dark_request": false }, - "Features": { + "features": { "tweetypie_unmention_optimization_enabled": true, "vibe_api_enabled": true, "responsive_web_edit_tweet_api_enabled": true, @@ -418,14 +430,31 @@ } }, "DeleteTweet": { - "Query": "VaenaVgh5q5ih7kvyVjgtg", - "Variables": { + "queryId": "VaenaVgh5q5ih7kvyVjgtg", + "variables": { "tweet_id": "1349129669258448897" - } + }, + "features": {} + }, + "create.json": { + "include_profile_interstitial_type": 1, + "include_blocking": 1, + "include_blocked_by": 1, + "include_followed_by": 1, + "include_want_retweets": 1, + "include_mute_edge": 1, + "include_can_dm": 1, + "include_can_media_tag": 1, + "include_ext_has_nft_avatar": 1, + "include_ext_is_blue_verified": 1, + "include_ext_verified_type": 1, + "include_ext_profile_image_shape": 1, + "skip_status": 1, + "user_id": 1180389371481976833 }, "Template": { - "Query": "", - "Variables": {}, - "Features": {} + "queryId": "", + "variables": {}, + "features": {} } } \ No newline at end of file diff --git a/src/config/variable.json b/src/config/variable.json deleted file mode 100644 index 38fc366..0000000 --- a/src/config/variable.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "docs": { - "header": "securitySchemes", - "get_parameters": "schema_content", - "post_parameters": "schema_parameters" - }, - "dart": { - "header": "parameters", - "get_parameters": "string", - "post_parameters": "object" - }, - "typescript": { - "header": "securitySchemes", - "get_parameters": "string", - "post_parameters": "object" - }, - "test": { - "header": "securitySchemes", - "get_parameters": "string", - "post_parameters": "string" - } -} \ No newline at end of file diff --git a/src/openapi/openapi-3.0.yaml b/src/openapi/openapi-3.0.yaml index e2a1da5..bcc6973 100644 --- a/src/openapi/openapi-3.0.yaml +++ b/src/openapi/openapi-3.0.yaml @@ -42,30 +42,6 @@ components: name: x-twitter-auth-type description: "OAuth2Session if you are logged in" - # {% if header == securitySchemes %} - - UserAgent: - description: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36" - in: header - name: user-agent - type: apiKey - ActiveUser: - description: "yes" - in: header - name: x-twitter-active-user - type: apiKey - BearerAuth: - description: AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA - scheme: bearer - type: http - ClientLanguage: - description: en - in: header - name: x-twitter-client-language - type: apiKey - - # {% endif %} - security: - bearerAuth: [] - CsrfToken: [] diff --git a/src/openapi/paths/bookmarks.yaml b/src/openapi/paths/bookmarks.yaml index 27d2915..e26c1ce 100644 --- a/src/openapi/paths/bookmarks.yaml +++ b/src/openapi/paths/bookmarks.yaml @@ -4,7 +4,7 @@ info: version: 0.0.1 paths: - /{{BookmarksQuery}}/Bookmarks: + /graphql/{{queryId}}/Bookmarks: get: operationId: getBookmarks description: get bookmarks @@ -16,7 +16,7 @@ paths: schema: $ref: "#/components/schemas/BookmarksResponse" tags: - - "Tweet" + - "tweet" components: schemas: diff --git a/src/openapi/paths/follow.yaml b/src/openapi/paths/follow.yaml index 54ae9d6..c0f6eeb 100644 --- a/src/openapi/paths/follow.yaml +++ b/src/openapi/paths/follow.yaml @@ -4,7 +4,7 @@ info: version: 0.0.1 paths: - /{{FollowingQuery}}/Following: + /graphql/{{queryId}}/Following: get: operationId: getFollowing description: get user list of following @@ -16,9 +16,9 @@ paths: schema: $ref: "#/components/schemas/FollowResponse" tags: - - "UserList" + - "userList" - /{{FollowersQuery}}/Followers: + /graphql/{{queryId}}/Followers: get: operationId: getFollowers description: get user list of followers @@ -30,7 +30,7 @@ paths: schema: $ref: "#/components/schemas/FollowResponse" tags: - - "UserList" + - "userList" components: schemas: diff --git a/src/openapi/paths/post.yaml b/src/openapi/paths/post.yaml index b24e344..c78b0e4 100644 --- a/src/openapi/paths/post.yaml +++ b/src/openapi/paths/post.yaml @@ -4,7 +4,7 @@ info: version: 0.0.1 paths: - /{{FavoriteTweetQuery}}/FavoriteTweet: + /graphql/{{queryId}}/FavoriteTweet: post: operationId: postFavoriteTweet description: favorite Tweet @@ -16,9 +16,9 @@ paths: schema: $ref: "#/components/schemas/FavoriteTweetResponseData" tags: - - "Post" + - "post" - /{{UnfavoriteTweetQuery}}/UnfavoriteTweet: + /graphql/{{queryId}}/UnfavoriteTweet: post: operationId: postUnfavoriteTweet description: unfavorite Tweet @@ -30,9 +30,9 @@ paths: schema: $ref: "#/components/schemas/UnfavoriteTweetResponseData" tags: - - "Post" + - "post" - /{{CreateRetweetQuery}}/CreateRetweet: + /graphql/{{queryId}}/CreateRetweet: post: operationId: postCreateRetweet description: create Retweet @@ -44,9 +44,9 @@ paths: schema: $ref: "#/components/schemas/CreateRetweetResponse" tags: - - "Post" + - "post" - /{{DeleteRetweetQuery}}/DeleteRetweet: + /graphql/{{queryId}}/DeleteRetweet: post: operationId: postDeleteRetweet description: delete Retweet @@ -58,9 +58,9 @@ paths: schema: $ref: "#/components/schemas/DeleteRetweetResponse" tags: - - "Post" + - "post" - /{{CreateTweetQuery}}/CreateTweet: + /graphql/{{queryId}}/CreateTweet: post: operationId: postCreateTweet description: create Tweet @@ -72,9 +72,9 @@ paths: schema: $ref: "#/components/schemas/CreateTweetResponse" tags: - - "Post" + - "post" - /{{DeleteTweetQuery}}/DeleteTweet: + /graphql/{{queryId}}/DeleteTweet: post: operationId: postDeleteTweet description: delete Retweet @@ -86,7 +86,7 @@ paths: schema: $ref: "#/components/schemas/DeleteTweetResponse" tags: - - "Post" + - "post" components: schemas: diff --git a/src/openapi/paths/profile.yaml b/src/openapi/paths/profile.yaml index 07ad419..67682fa 100644 --- a/src/openapi/paths/profile.yaml +++ b/src/openapi/paths/profile.yaml @@ -4,7 +4,7 @@ info: version: 0.0.1 paths: - /{{ProfileSpotlightsQueryQuery}}/ProfileSpotlightsQuery: + /graphql/{{queryId}}/ProfileSpotlightsQuery: get: operationId: getProfileSpotlightsQuery description: "get user by screen name" diff --git a/src/openapi/paths/timeline.yaml b/src/openapi/paths/timeline.yaml index e41fb64..12a4345 100644 --- a/src/openapi/paths/timeline.yaml +++ b/src/openapi/paths/timeline.yaml @@ -4,7 +4,7 @@ info: version: 0.0.1 paths: - /{{HomeTimelineQuery}}/HomeTimeline: + /graphql/{{queryId}}/HomeTimeline: get: operationId: getHomeTimeline description: get tweet list of timeline @@ -16,9 +16,9 @@ paths: schema: $ref: "#/components/schemas/TimelineResponse" tags: - - "Tweet" + - "tweet" - /{{HomeLatestTimelineQuery}}/HomeLatestTimeline: + /graphql/{{queryId}}/HomeLatestTimeline: get: operationId: getHomeLatestTimeline description: get tweet list of timeline @@ -30,9 +30,9 @@ paths: schema: $ref: "#/components/schemas/TimelineResponse" tags: - - "Tweet" + - "tweet" - /{{ListLatestTweetsTimelineQuery}}/ListLatestTweetsTimeline: + /graphql/{{queryId}}/ListLatestTweetsTimeline: get: operationId: getListLatestTweetsTimeline description: get tweet list of timeline @@ -44,7 +44,7 @@ paths: schema: $ref: "#/components/schemas/ListTweetsTimelineResponse" tags: - - "Tweet" + - "tweet" components: schemas: diff --git a/src/openapi/paths/tweet.yaml b/src/openapi/paths/tweet.yaml index 24b3d0b..9f0539f 100644 --- a/src/openapi/paths/tweet.yaml +++ b/src/openapi/paths/tweet.yaml @@ -4,7 +4,7 @@ info: version: 0.0.1 paths: - /{{TweetDetailQuery}}/TweetDetail: + /graphql/{{queryId}}/TweetDetail: get: operationId: getTweetDetail description: get TweetDetail @@ -16,7 +16,7 @@ paths: schema: $ref: "#/components/schemas/TweetDetailResponse" tags: - - "Tweet" + - "tweet" components: schemas: diff --git a/src/openapi/paths/user.yaml b/src/openapi/paths/user.yaml index 38affb2..874bd13 100644 --- a/src/openapi/paths/user.yaml +++ b/src/openapi/paths/user.yaml @@ -4,7 +4,7 @@ info: version: 0.0.1 paths: - /{{UserByScreenNameQuery}}/UserByScreenName: + /graphql/{{queryId}}/UserByScreenName: get: operationId: getUserByScreenName description: "get user by screen name" @@ -16,7 +16,7 @@ paths: schema: $ref: "#/components/schemas/UserResponse" tags: - - "User" + - "user" components: schemas: diff --git a/src/openapi/paths/usertweets.yaml b/src/openapi/paths/usertweets.yaml index 0bdc02d..c1013d0 100644 --- a/src/openapi/paths/usertweets.yaml +++ b/src/openapi/paths/usertweets.yaml @@ -4,7 +4,7 @@ info: version: 0.0.1 paths: - /{{UserTweetsQuery}}/UserTweets: + /graphql/{{queryId}}/UserTweets: get: operationId: getUserTweets description: "get user tweets" @@ -16,9 +16,9 @@ paths: schema: $ref: "#/components/schemas/UserTweetsResponse" tags: - - "Tweet" + - "tweet" - /{{UserTweetsAndRepliesQuery}}/UserTweetsAndReplies: + /graphql/{{queryId}}/UserTweetsAndReplies: get: operationId: getUserTweetsAndReplies description: "get user replies tweets" @@ -30,9 +30,9 @@ paths: schema: $ref: "#/components/schemas/UserTweetsResponse" tags: - - "Tweet" + - "tweet" - /{{UserMediaQuery}}/UserMedia: + /graphql/{{queryId}}/UserMedia: get: operationId: getUserMedia description: "get user media tweets" @@ -44,9 +44,9 @@ paths: schema: $ref: "#/components/schemas/UserTweetsResponse" tags: - - "Tweet" + - "tweet" - /{{LikesQuery}}/Likes: + /graphql/{{queryId}}/Likes: get: operationId: getLikes description: "get user likes tweets" @@ -58,7 +58,7 @@ paths: schema: $ref: "#/components/schemas/UserTweetsResponse" tags: - - "Tweet" + - "tweet" components: schemas: diff --git a/src/openapi/paths/v1.1.yaml b/src/openapi/paths/v1.1.yaml new file mode 100644 index 0000000..56d1398 --- /dev/null +++ b/src/openapi/paths/v1.1.yaml @@ -0,0 +1,15 @@ +openapi: 3.0.3 +info: + title: Twitter OpenAPI + version: 0.0.1 + +paths: + /1.1/friendships/create.json: + post: + operationId: postCreateFriendships + description: post create friendships + responses: + "200": + description: Successful operation + tags: + - "v1.1" diff --git a/tools/build.py b/tools/build.py index dda87ff..ab39d16 100644 --- a/tools/build.py +++ b/tools/build.py @@ -5,205 +5,55 @@ import yaml import shutil import copy import re - - -class placeholder_manager: - data: dict - config: str = "src/config/placeholder.json" - - def __init__(self): - with open(self.config, mode="r", encoding="utf-8") as f: - self.data = json.load(f) - - def __replace(self, file, old: str, new) -> str: - if type(new) is dict: - dump = f"'{json.dumps(new)}'" - return file.replace(f'"{old}"', dump) - else: - return file.replace(f"{old}", new) - - def replace(self, file: str) -> str: - for prefix in self.data.keys(): - for key in self.data[prefix]: - value = self.data[prefix][key] - file = self.__replace(file, f"{{{{{prefix}{key}}}}}", value) - return file - - def replace_file(self, file: str, prefix: str) -> str: - if self.data.get(prefix, None) is None: - return file - for key in self.data[prefix].keys(): - value = self.data[prefix][key] - file = self.__replace(file, f"{{{{{key}}}}}", value) - return file - - -def placeholder_to_yaml(obj): - if type(obj) is dict: - return { - "type": "object", - "required": [i for i in obj], - "properties": {i: placeholder_to_yaml(obj[i]) for i in obj}, - # "default": {i: placeholder_to_yaml(obj[i]) for i in obj}, - # "example": {i: placeholder_to_yaml(obj[i]) for i in obj}, - } - elif type(obj) is list: - if len(obj) == 0: - return { - "type": "array", - "items": {"type": "object"}, - # "default": [], - # "example": [], - } - return { - "type": "array", - "items": placeholder_to_yaml(obj[0]), - # "default": placeholder_to_yaml(obj[0]), - # "example": placeholder_to_yaml(obj[0]), - } - elif type(obj) is str: - return {"type": "string", "example": obj, "default": obj} - elif type(obj) is bool: - return {"type": "boolean", "example": obj, "default": obj} - elif type(obj) is int: - return {"type": "integer", "example": obj, "default": obj} +from build_config import Config print("=== Build Start ===") -OUTPUT_DIR = "dist/{0}" -INPUT_DIR = "src/openapi" -METHODS = ["get", "put", "post", "delete", "options", "head", "patch", "trace"] + + +config = Config() + try: shutil.rmtree("dist") except: pass -with open("src/config/variable.json", mode="r", encoding="utf-8") as f: - variable = json.load(f) +for lang in config.main().keys(): + dist_replace = lambda x: x.replace( + config.INPUT_DIR, config.OUTPUT_DIR.format(lang), 1 + ) - -placeholder = placeholder_manager() - -for lang in variable.keys(): - - def read(file: str): - with open(file, mode="r", encoding="utf-8") as f: - return remove(f.read()) - - def write(file: str, data: str) -> None: - with open( - file.replace(INPUT_DIR, OUTPUT_DIR.format(lang), 1), - mode="w+", - encoding="utf-8", - ) as f: - f.write(data) - - def get_yaml(data, key): - return yaml.safe_load(placeholder.replace_file(str(data), key)) - - def remove(data): - for match in re.findall(r"(\{% (.*?) %\})", data): - equation = match[1].split(" ") - if equation[0] == "if" and equation[2] == "==": - if equation[3] != variable[lang][equation[1]]: - data = re.sub( - re.escape(match[0]) + "[\s\S]*?" + re.escape("{% endif %}"), - "", - data, - ) - - return data - - for dir in glob.glob(os.path.join(INPUT_DIR, "**/")): - os.makedirs(dir.replace(INPUT_DIR, OUTPUT_DIR.format(lang), 1), exist_ok=True) - - parameters = read("src/config/parameters.yaml") - header = read("src/config/header.yaml") + for dir in glob.glob(os.path.join(config.INPUT_DIR, "**/")): + os.makedirs(dist_replace(dir), exist_ok=True) paths = {} - for file in glob.glob(os.path.join(INPUT_DIR, "**/*.yaml")): + for file in glob.glob(os.path.join(config.INPUT_DIR, "**/*.yaml")): file = file.replace(os.path.sep, "/") - relative = file.replace(INPUT_DIR, "", 1) + with open(file, mode="r", encoding="utf-8") as f: + load = yaml.safe_load(f) + for path in list(load["paths"]): + for method in list(load["paths"][path]): + for tag in list(load["paths"][path][method].get("tags", ["default"])): + key, value = path, load["paths"][path][method] + for hook in config.main()[lang]["request"][tag]: + key, value = hook.hook(key, value) + load["paths"][path][method] = value + load["paths"][key] = load["paths"].pop(path) - load = yaml.safe_load(placeholder.replace(read(file))) + escape = path.replace("/", "~1") + relative = file.replace(config.INPUT_DIR, "", 1) + paths.update({key: {"$ref": f".{relative}#/paths/{escape}"}}) - for key in load["paths"].keys(): - append = get_yaml(parameters, key.split("/")[-1]) - param = append["paths"]["/parameters"] - for method in load["paths"][key].keys(): - req = load["paths"][key][method] - - req["parameters"] = req.get("parameters", []) - req["parameters"] += param[method].get("parameters", []) - - if param[method].get("requestBody") is not None: - req["requestBody"] = param[method].get("requestBody") - - if variable[lang].get(method + "_parameters") == "schema_content": - for p_key in placeholder.data[key.split("/")[-1]].keys(): - if p_key.lower() == "query": - continue - req["parameters"].append( - { - "name": p_key.lower(), - "in": "query", - "required": True, - "content": { - "application/json": { - "schema": placeholder_to_yaml( - placeholder.data[key.split("/")[-1]][p_key] - ), - }, - }, - } - ) - - if variable[lang].get(method + "_parameters") == "schema_parameters": - for p_key in placeholder.data[key.split("/")[-1]].keys(): - if p_key.lower() == "query": - continue - req["parameters"].append( - { - "name": p_key.lower(), - "in": "query", - "required": True, - "schema": placeholder_to_yaml( - placeholder.data[key.split("/")[-1]][p_key] - ), - } - ) - - if variable[lang].get(method + "_parameters") == "schema_request_body": - data = placeholder.data[key.split("/")[-1]] - schema = {i: placeholder_to_yaml(data[i]) for i in data.keys()} - - req["requestBody"] = { - "description": key.split("/")[-1] + "body", - "required": True, - "content": { - "application/json": { - "schema": { - "properties": schema, - }, - } - }, - } - - append = get_yaml(header, key.split("/")[-1]) - req = load["paths"][key][method] - req["responses"]["200"]["headers"] = append["components"]["headers"] - - escape = key.replace("/", "~1") - paths.update({key: {"$ref": f".{relative}#/paths/{escape}"}}) - write(file, yaml.dump(load)) + with open(dist_replace(file), mode="w+", encoding="utf-8") as f: + f.write(yaml.dump(load)) file = "src/openapi/openapi-3.0.yaml" - data = read(file) + with open(file, mode="r", encoding="utf-8") as f: + openapi = yaml.safe_load(f) for path in paths: - load = yaml.safe_load(placeholder.replace(data)) load["paths"] = paths - - write(file, yaml.dump(load)) + with open(dist_replace(file), mode="w+", encoding="utf-8") as f: + f.write(yaml.dump(load)) print("=== Build End ===") diff --git a/tools/build_config.py b/tools/build_config.py new file mode 100644 index 0000000..94dceaa --- /dev/null +++ b/tools/build_config.py @@ -0,0 +1,111 @@ +from hooks import * + + +class Config: + OUTPUT_DIR = "dist/{0}" + INPUT_DIR = "src/openapi" + + def main(self): + return { + "docs": { + "openapi": [AddSecuritySchemesOnSecuritySchemes()], + "request": { + key: [ + ReplaceQueryIdPlaceholder(), + SetResponsesHeader(), + AddParametersOnContent(), + ] + for key in ["default", "user", "userList", "tweet"] + } + | { + "post": [ + ReplaceQueryIdPlaceholder(), + SetResponsesHeader(), + AddParametersOnParameters(), + ], + "v1.1": [SetResponsesHeader()], + }, + }, + "dart": { + "openapi": [], + "request": { + key: [ + ReplaceQueryIdPlaceholder(), + AddSecuritySchemesOnHeader(), + SetResponsesHeader(), + AddParametersOnParametersAsString(), + ] + for key in ["default", "user", "userList", "tweet"] + } + | { + "post": [ + ReplaceQueryIdPlaceholder(), + AddSecuritySchemesOnHeader(), + SetResponsesHeader(), + AddParametersOnParametersAsObject(), + ], + "v1.1": [SetResponsesHeader()], + }, + }, + "typescript": { + "openapi": [AddSecuritySchemesOnSecuritySchemes()], + "request": { + key: [ + ReplaceQueryIdPlaceholder(), + SetResponsesHeader(), + AddParametersOnParametersAsString(), + ] + for key in ["default", "user", "userList", "tweet"] + } + | { + "post": [ + ReplaceQueryIdPlaceholder(), + SetResponsesHeader(), + AddParametersOnParametersAsObject(), + ], + "v1.1": [SetResponsesHeader()], + }, + }, + "test": { + "openapi": [AddSecuritySchemesOnSecuritySchemes()], + "request": { + key: [ + ReplaceQueryIdPlaceholder(), + SetResponsesHeader(), + AddParametersOnParametersAsString(), + ] + for key in ["default", "user", "userList", "tweet"] + } + | { + "post": [ + ReplaceQueryIdPlaceholder(), + SetResponsesHeader(), + AddParametersOnParametersAsString(), + ], + "v1.1": [SetResponsesHeader()], + }, + }, + } + + """ + "docs": { + "header": "securitySchemes", + "get_parameters": "schema_content", + "post_parameters": "schema_parameters" + }, + "dart": { + "header": "parameters", + "get_parameters": "string", + "post_parameters": "object" + }, + "typescript": { + "header": "securitySchemes", + "get_parameters": "string", + "post_parameters": "object" + }, + "test": { + "header": "securitySchemes", + "get_parameters": "string", + "post_parameters": "string" + } + """ diff --git a/tools/hooks.py b/tools/hooks.py new file mode 100644 index 0000000..a9fcffa --- /dev/null +++ b/tools/hooks.py @@ -0,0 +1,193 @@ +import yaml +import json + + +class HookBase: + PLACEHOLDER: dict + + def __init__(self): + with open("src/config/placeholder.json", mode="r", encoding="utf-8") as f: + self.PLACEHOLDER = json.load(f) + + def placeholder_to_yaml(self, obj, default=False, example=False) -> dict: + fn = lambda x: self.placeholder_to_yaml(x, default=default, example=example) + if type(obj) is dict: + properties = {i: fn(obj[i]) for i in obj} + value = { + "type": "object", + "required": [i for i in obj], + "properties": properties, + } + value.update({"default": properties} if default else {}) + value.update({"example": properties} if example else {}) + return value + elif type(obj) is list and len(obj) > 0: + properties = fn(obj[0]) + value = { + "type": "array", + "items": properties, + } + value.update({"default": [properties]} if default else {}) + value.update({"example": [properties]} if example else {}) + return value + elif type(obj) is list and len(obj) == 0: + value = { + "type": "array", + "items": {"type": "object"}, + } + value.update({"default": []} if default else {}) + value.update({"example": []} if example else {}) + return value + elif type(obj) is str: + return {"type": "string", "example": obj, "default": obj} + elif type(obj) is bool: + return {"type": "boolean", "example": obj, "default": obj} + elif type(obj) is int: + return {"type": "integer", "example": obj, "default": obj} + + def load_component(self, name: str) -> dict: + with open(f"src/config/component/{name}.yaml", mode="r", encoding="utf-8") as f: + return yaml.safe_load(f) + + +class OpenapiHookBase(HookBase): + def hook(self, value: dict): + return value + + +class RequestHookBase(HookBase): + def hook(self, path: str, value: dict): + return path, value + + +class AddSecuritySchemesOnSecuritySchemes(HookBase): + def hook(self, value: dict): + component = self.load_component("security_schemes") + param = component["components"]["securitySchemes"] + value["components"]["securitySchemes"].extend(param) + return value + + +class AddSecuritySchemesOnHeader(RequestHookBase): + def hook(self, path: str, value: dict): + value["parameters"] = value.get("parameters", []) + component = self.load_component("security_schemes") + param = component["paths"]["/parameters"]["get"]["parameters"] + value["parameters"].extend(param) + return path, value + + +class ReplaceQueryIdPlaceholder(RequestHookBase): + def hook(self, path: str, value: dict): + path_name = path.split("/")[-1] + new = self.PLACEHOLDER[path_name]["queryId"] + return path.replace(r"{{queryId}}", new), value + + +class SetResponsesHeader(RequestHookBase): + def hook(self, path: str, value: dict): + component = self.load_component("response_header") + value["responses"]["200"]["headers"] = component["components"]["headers"] + return path, value + + +class AddParametersOnParametersAsString(RequestHookBase): + def hook(self, path: str, value: dict): + value["parameters"] = value.get("parameters", []) + path_name = path.split("/")[-1] + for key in self.PLACEHOLDER[path_name].keys(): + example = json.dumps(self.PLACEHOLDER[path_name][key]) + value["parameters"].append( + { + "name": key, + "in": "query", + "required": True, + "schema": { + "type": "string", + "default": example, + "example": example, + }, + } + ) + return path, value + + +class AddParametersOnParametersAsObject(RequestHookBase): + def hook(self, path: str, value: dict): + value["parameters"] = value.get("parameters", []) + path_name = path.split("/")[-1] + for key in self.PLACEHOLDER[path_name].keys(): + example = json.dumps(self.PLACEHOLDER[path_name][key]) + value["parameters"].append( + { + "name": key, + "in": "query", + "required": True, + "schema": { + "type": "object", + "default": example, + "example": example, + }, + } + ) + return path, value + + +class AddParametersOnContent(RequestHookBase): + def hook(self, path: str, value: dict): + value["parameters"] = value.get("parameters", []) + path_name = path.split("/")[-1] + for key in self.PLACEHOLDER[path_name].keys(): + value["parameters"].append( + { + "name": key, + "in": "query", + "required": True, + "content": { + "application/json": { + "schema": self.placeholder_to_yaml( + self.PLACEHOLDER[path_name][key] + ), + }, + }, + } + ) + return path, value + + +class AddParametersOnParameters(RequestHookBase): + def hook(self, path: str, value: dict): + value["parameters"] = value.get("parameters", []) + path_name = path.split("/")[-1] + for key in self.PLACEHOLDER[path_name].keys(): + value["parameters"].append( + { + "name": key, + "in": "query", + "required": True, + "schema": self.placeholder_to_yaml( + self.PLACEHOLDER[path_name][key] + ), + } + ) + return path, value + + +class AddParametersOnBody(RequestHookBase): + def hook(self, path: str, value: dict): + value["parameters"] = value.get("parameters", []) + path_name = path.split("/")[-1] + data = self.PLACEHOLDER[path_name] + schema = {i: self.placeholder_to_yaml(data[i]) for i in data.keys()} + value["requestBody"] = { + "description": "body", + "required": True, + "content": { + "application/json": { + "schema": { + "properties": schema, + }, + } + }, + } + return path, value