Escape-Technologies / graphman

Quikly scaffold a postman collection for a GraphQL API. Compatible with Postman & Insomnia.
MIT License
240 stars 12 forks source link

feat: make graphql pretty #5

Closed notrab closed 2 years ago

notrab commented 2 years ago

I ran the final output through the print/parse functions of graphql to remove any unwanted spaces. This should mean we can simplify some of the formatting functions, and rely on this, as well as JSON.stringify later (for variables, etc.)

lmkwyt

nohehf commented 2 years ago

Hello @notrab ! Thanks, again for the MR. However I can't merge right now cause it failed to pass one test (I will create a CI / CD for this in the future). It crashes on the anilist GraphQL API: https://graphql.anilist.co/ . image

The previous version don't: image

Also, I've seen that you changed some linting, what formater did you use? I would like to keep this consistent at some point. I was using the deno linting / formatting but I can agree that it's not really great.

The issue seems to be related to the graphql parser.

notrab commented 2 years ago

Hey @nohehf

Thanks for checking this over. I'll take a look at the above, and get back to you.

As for formatting, I guess this was done by my Prettier extension. I've never used Deno before, so I wasn't aware of any built-in fmt-like utility. I'll sort that too.

notrab commented 2 years ago

It would appear before my changes it also generated an invalid schema. It just so happens there was no check on this.

CleanShot 2022-07-13 at 09 15 26@2x
nohehf commented 2 years ago

Oh okay alright, the issue is that object fields are commented by default, so if you have a query field with only object fields the generated query will be empty so incorrect. This choice was made to prevent infinite nested query fields. Every generated request is not valid by default but human-readable and easily editable. I don't really know how to patch this without changing this choice, which I think is important. I don't think the goal of GraphMan should be to generate valid queries all the time, tho this prevents doing clean graphql parsing. Let me know your thoughts abt this, I'll be happy to discuss it.

notrab commented 2 years ago

@nohehf perhaps we could include __typename by default in all queries so it's at least valid?

notrab commented 2 years ago

@nohehf I added __typename and ran deno fmt.

lmkwyt

nohehf commented 2 years ago

I still have some concerns with using the graphql parser: I had done some custom parsing so that arguments are line by line if there are too many for example. Indeed I do feel that even not standard this is much more redable:

query(
    $mediaId: Int,
    $date: Int,
    $trending: Int,
    $averageScore: Int,
    $popularity: Int,
    $episode: Int,
    $releasing: Boolean,
    $mediaId_not: Int,
    $mediaId_in: [Int],
    $mediaId_not_in: [Int],
    $date_greater: Int,
    $date_lesser: Int,
    $trending_greater: Int,
    $trending_lesser: Int,
    $trending_not: Int,
    $averageScore_greater: Int,
    $averageScore_lesser: Int,
    $averageScore_not: Int,
    $popularity_greater: Int,
    $popularity_lesser: Int,
    $popularity_not: Int,
    $episode_greater: Int,
    $episode_lesser: Int,
    $episode_not: Int,
    $sort: [MediaTrendSort]
){
    MediaTrend(
        mediaId: $mediaId, 
        date: $date, 
        trending: $trending, 
        averageScore: $averageScore, 
        popularity: $popularity, 
        episode: $episode, 
        releasing: $releasing, 
        mediaId_not: $mediaId_not, 
        mediaId_in: $mediaId_in, 
        mediaId_not_in: $mediaId_not_in, 
        date_greater: $date_greater, 
        date_lesser: $date_lesser, 
        trending_greater: $trending_greater, 
        trending_lesser: $trending_lesser, 
        trending_not: $trending_not, 
        averageScore_greater: $averageScore_greater, 
        averageScore_lesser: $averageScore_lesser, 
        averageScore_not: $averageScore_not, 
        popularity_greater: $popularity_greater, 
        popularity_lesser: $popularity_lesser, 
        popularity_not: $popularity_not, 
        episode_greater: $episode_greater, 
        episode_lesser: $episode_lesser, 
        episode_not: $episode_not, 
        sort: $sort
    ){
        mediaId # The id of the tag
        date # The day the data was recorded (timestamp)
        trending # The amount of media activity on the day
        averageScore # A weighted average score of all the user's scores of the media
        popularity # The number of users with the media on their list
        inProgress # The number of users with watching/reading the media
        releasing # If the media was being released at this time
        episode # The episode number of the anime released on this day
        # media # The related media
    }
}

than

query MediaTrend($mediaId: Int, $date: Int, $trending: Int, $averageScore: Int, $popularity: Int, $episode: Int, $releasing: Boolean, $mediaId_not: Int, $mediaId_in: [Int], $mediaId_not_in: [Int], $date_greater: Int, $date_lesser: Int, $trending_greater: Int, $trending_lesser: Int, $trending_not: Int, $averageScore_greater: Int, $averageScore_lesser: Int, $averageScore_not: Int, $popularity_greater: Int, $popularity_lesser: Int, $popularity_not: Int, $episode_greater: Int, $episode_lesser: Int, $episode_not: Int, $sort: [MediaTrendSort]) {
  MediaTrend(
    mediaId: $mediaId
    date: $date
    trending: $trending
    averageScore: $averageScore
    popularity: $popularity
    episode: $episode
    releasing: $releasing
    mediaId_not: $mediaId_not
    mediaId_in: $mediaId_in
    mediaId_not_in: $mediaId_not_in
    date_greater: $date_greater
    date_lesser: $date_lesser
    trending_greater: $trending_greater
    trending_lesser: $trending_lesser
    trending_not: $trending_not
    averageScore_greater: $averageScore_greater
    averageScore_lesser: $averageScore_lesser
    averageScore_not: $averageScore_not
    popularity_greater: $popularity_greater
    popularity_lesser: $popularity_lesser
    popularity_not: $popularity_not
    episode_greater: $episode_greater
    episode_lesser: $episode_lesser
    episode_not: $episode_not
    sort: $sort
  ) {
    __typename
    mediaId
    date
    trending
    averageScore
    popularity
    inProgress
    releasing
    episode
  }
}

But lmkwyt about this, not sure of my opinion after writing it.

I also noticed an issue with the Page query with your changes. while there is the _typename field, you removed the other fields that should be commented:

query Page($page: Int, $perPage: Int) {
  Page(page: $page, perPage: $perPage) {
    __typename
  }
}

should be (in terms of content, not formatting):

query( $page: Int, $perPage: Int){
    Page( page: $page,  perPage: $perPage){
                 __typename
        # pageInfo # The pagination information
        # users     
                # media     
                # characters        
                # staff     
                # studios       
                # mediaList     
                # airingSchedules       
                # mediaTrends       
                # notifications # Type: LIST
                # followers     
                # following     
                # activities # Type: LIST
            # activityReplies       
                # threads       
                # threadComments        
                # reviews       
                # recommendations       
                # likes 
    }
}
nohehf commented 2 years ago

Okay so I investigated a bit, and it seems like graphql.parse() removes comments, and there is no parameter to prevent that, so the only solution I see would be to add them after the parsing tho it might be a huge mess. Let me know if you have any thoughts on this

nohehf commented 2 years ago

Okay, so I've found a workaround. I generate a random hash for every field before the parsing, so the query gets parsed and formatted correctly, (see tempField). I then replace all of those hashes with the correct field (with comments). The caveat is that some parts of the query are not properly parsed, but it's the best solution I think. So now we have:

query Media($id: Int, $idMal: Int, $startDate: FuzzyDateInt, $endDate: FuzzyDateInt, $season: MediaSeason, $seasonYear: Int, $type: MediaType, $format: MediaFormat, $status: MediaStatus, $episodes: Int, $duration: Int, $chapters: Int, $volumes: Int, $isAdult: Boolean, $genre: String, $tag: String, $minimumTagRank: Int, $tagCategory: String, $onList: Boolean, $licensedBy: String, $licensedById: Int, $averageScore: Int, $popularity: Int, $source: MediaSource, $countryOfOrigin: CountryCode, $isLicensed: Boolean, $search: String, $id_not: Int, $id_in: [Int], $id_not_in: [Int], $idMal_not: Int, $idMal_in: [Int], $idMal_not_in: [Int], $startDate_greater: FuzzyDateInt, $startDate_lesser: FuzzyDateInt, $startDate_like: String, $endDate_greater: FuzzyDateInt, $endDate_lesser: FuzzyDateInt, $endDate_like: String, $format_in: [MediaFormat], $format_not: MediaFormat, $format_not_in: [MediaFormat], $status_in: [MediaStatus], $status_not: MediaStatus, $status_not_in: [MediaStatus], $episodes_greater: Int, $episodes_lesser: Int, $duration_greater: Int, $duration_lesser: Int, $chapters_greater: Int, $chapters_lesser: Int, $volumes_greater: Int, $volumes_lesser: Int, $genre_in: [String], $genre_not_in: [String], $tag_in: [String], $tag_not_in: [String], $tagCategory_in: [String], $tagCategory_not_in: [String], $licensedBy_in: [String], $licensedById_in: [Int], $averageScore_not: Int, $averageScore_greater: Int, $averageScore_lesser: Int, $popularity_not: Int, $popularity_greater: Int, $popularity_lesser: Int, $source_in: [MediaSource], $sort: [MediaSort]) {
  Media(
    id: $id
    idMal: $idMal
    startDate: $startDate
    endDate: $endDate
    season: $season
    seasonYear: $seasonYear
    type: $type
    format: $format
    status: $status
    episodes: $episodes
    duration: $duration
    chapters: $chapters
    volumes: $volumes
    isAdult: $isAdult
    genre: $genre
    tag: $tag
    minimumTagRank: $minimumTagRank
    tagCategory: $tagCategory
    onList: $onList
    licensedBy: $licensedBy
    licensedById: $licensedById
    averageScore: $averageScore
    popularity: $popularity
    source: $source
    countryOfOrigin: $countryOfOrigin
    isLicensed: $isLicensed
    search: $search
    id_not: $id_not
    id_in: $id_in
    id_not_in: $id_not_in
    idMal_not: $idMal_not
    idMal_in: $idMal_in
    idMal_not_in: $idMal_not_in
    startDate_greater: $startDate_greater
    startDate_lesser: $startDate_lesser
    startDate_like: $startDate_like
    endDate_greater: $endDate_greater
    endDate_lesser: $endDate_lesser
    endDate_like: $endDate_like
    format_in: $format_in
    format_not: $format_not
    format_not_in: $format_not_in
    status_in: $status_in
    status_not: $status_not
    status_not_in: $status_not_in
    episodes_greater: $episodes_greater
    episodes_lesser: $episodes_lesser
    duration_greater: $duration_greater
    duration_lesser: $duration_lesser
    chapters_greater: $chapters_greater
    chapters_lesser: $chapters_lesser
    volumes_greater: $volumes_greater
    volumes_lesser: $volumes_lesser
    genre_in: $genre_in
    genre_not_in: $genre_not_in
    tag_in: $tag_in
    tag_not_in: $tag_not_in
    tagCategory_in: $tagCategory_in
    tagCategory_not_in: $tagCategory_not_in
    licensedBy_in: $licensedBy_in
    licensedById_in: $licensedById_in
    averageScore_not: $averageScore_not
    averageScore_greater: $averageScore_greater
    averageScore_lesser: $averageScore_lesser
    popularity_not: $popularity_not
    popularity_greater: $popularity_greater
    popularity_lesser: $popularity_lesser
    source_in: $source_in
    sort: $sort
  ) {
    __typename
    id # The id of the media
    idMal # The mal id of the media
    # title # The official titles of the media in various languages
    type # The type of the media; anime or manga
    format # The format the media was released in
    status # The current releasing status of the media
    description # Short description of the media's story and characters
    # startDate # The first official release date of the media
    # endDate # The last official release date of the media
    season # The season the media was initially released in
    seasonYear # The season year the media was initially released in
    seasonInt # The year & season the media was initially released in
    episodes # The amount of episodes the anime has when complete
    duration # The general length of each anime episode in minutes
    chapters # The amount of chapters the manga has when complete
    volumes # The amount of volumes the manga has when complete
    countryOfOrigin # Where the media was created. (ISO 3166-1 alpha-2)
    isLicensed # If the media is officially licensed or a self-published doujin release
    source # Source type the media was adapted from.
    hashtag # Official Twitter hashtags for the media
    # trailer # Media trailer or advertisement
    updatedAt # When the media's data was last updated
    # coverImage # The cover images of the media
    bannerImage # The banner image of the media
    genres # The genres of the media
    synonyms # Alternative titles of the media
    averageScore # A weighted average score of all the user's scores of the media
    meanScore # Mean score of all the user's scores of the media
    popularity # The number of users with the media on their list
    isLocked # Locked media may not be added to lists our favorited. This may be due to the entry pending for deletion or other reasons.
    trending # The amount of related activity in the past hour
    favourites # The amount of user's who have favourited the media
    # tags # List of tags that describes elements and themes of the media
    # relations # Other media in the same or connecting franchise
    # characters
    # staff
    # studios
    isFavourite # If the media is marked as favourite by the current authenticated user
    isFavouriteBlocked # If the media is blocked from being added to favourites
    isAdult # If the media is intended only for 18+ adult audiences
    # nextAiringEpisode # The media's next episode airing schedule
    # airingSchedule # The media's entire airing schedule
    # trends # The media's daily trend stats
    # externalLinks # External links to another site related to the media
    # streamingEpisodes # Data and links to legal streaming episodes on external sites
    # rankings # The ranking of the media in a particular time span and format compared to other media
    # mediaListEntry # The authenticated user's media list entry for the media
    # reviews
    # recommendations
    # stats
    siteUrl # The url for the media page on the AniList website
    autoCreateForumThread # If the media should have forum thread automatically created for it on airing episode release
    isRecommendationBlocked # If the media is blocked from being recommended to/from
    isReviewBlocked # If the media is blocked from being reviewed
    modNotes # Notes for site moderators
  }
}
nohehf commented 2 years ago

I'll clean this, remove unused manual formatting and merge. Ty @notrab

notrab commented 2 years ago

@nohehf wow! Nice. Sorry, I was away from my computer for a few hours today so only just catching up on notifications now. Thanks for the fixes! 😍