Git Product home page Git Product logo

api.serlo.org's People

Contributors

anbestcl avatar andreashuber avatar carolinjaser avatar coderj001 avatar codingdive avatar dependabot[bot] avatar elbotho avatar eliflores avatar hejtful avatar hugotiburtino avatar imac7 avatar imbhaskarn avatar inyono avatar kulla avatar larstheglidingsquirrel avatar lodifice avatar manuelmetzger avatar mmitzko avatar moehome avatar resirudo avatar shn-srl avatar shnbln avatar simonline avatar vroland avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

api.serlo.org's Issues

☂️ Umbrella Issue: Future-proof handling of connections

Pretty much all types that return a collection should be refactored to return a Relay Connection so that we can handle pagination in the future without breaking stuff. Furthermore, we might want to review GitHub's GraphQL API since they seem to extend the idea of Relay Connections:

{
  securityVulnerabilities(first: 5) {
    totalCount
    edges {
      __typename
      cursor
      node {
        advisory {
          id
        }
      }
    }
    nodes {
      advisory {
        id
      }
    }
  }
}

Basically, every collection in the GraphQL API returns the following:

  • totalCount: the total number of items in the collection
  • edges: the typical Relay Connection type w/ cursors and stuff
  • nodes: a helper that returns the same thing as edges but w/o cursors (if you don't have a need for pagination)

More specifically, we still use legacy collections in these locations:

Add discriminator to server payloads

Currently, we add the discriminator each time in set* in SerloDataSource. Instead, we should just add them to the server response instead so that we can get rid of the custom behavior.

Cache is not set correctly for GroupedExercise

{
  uuid(id: 57201) {
    __typename
    ... on GroupedExercise {
      currentRevision {
        id
        content
      }
    }
  }
}

still returns the previous revision, although a new revision was checked out a couple of days ago.

Sentry: pass environment

Currently, we can't differentiate Sentry issues by env. We should add that somehow (e.g. via env variable or something).

Query _getCacheKeys

Idea: services communicate which cache keys they can have so that we can use the information to fill the cache beforehand.

Basically, the GraphQL types should look like this:

extend type Query {
  _getCacheKeys(
    after: String
    before: String
    first: Int
    last: Int
  ): Query_GetCacheResult!
}

type Query_GetCacheResult {
  edges: [CacheKeyCursor!]!
  nodes: [String!]!
  totalCount: Int!
  pageInfo: PageInfo!
}

type CacheKeyCursor {
  cursor: String!
  node: String!
}

See src/graphql/schema/notification for an example on how to work with connections.

Furthermore, we need a legacy API endpoint that returns the cache keys for serlo.org:

  • GET /api/cache-keys returns an array of strings (i.e. the cache keys). This request won't be instance-aware, so it should return cache keys from all instances.

Checklist

  • Add _getCacheKeys to src/graphql/schema/cache.
  • Add contract test for _getCacheKeys that uses /api/cache-keys.

Add ArrayLike type for payloads

See serlo/serlo.org-legacy#474. Zend sometimes serializes arrays as objects (i.e. Record<number, unknown>). We should be able to handle those in payloads everywhere (we already handle this somewhere for notifications) since we apparently can't configure Zend to serialize stuff correctly w/o writing our own serializer...

Review navigation handling in staging

{
  uuid(alias:{ instance:de, path: "/mathe/zahlen-größen/prozent--zinsrechnung/prozent"}) {
    id
    __typename
    ... on Article {
      taxonomyTerms {
        id
        navigation {
          data
        }
      }
    }
  }
}

doesn't work in staging (vs. production)

Fix local playground

    {
      "message": "403: Forbidden",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "uuid"
      ],
      "extensions": {
        "code": "FORBIDDEN",
        "response": {
          "url": "https://de.serlo.org/api/uuid/1855",
          "status": 403,
          "statusText": "Forbidden",
          "body": {
            "reason": "Invalid authorization header"
          }
        },
        "exception": {
          "stacktrace": [
            "ForbiddenError: 403: Forbidden",
            "    at SerloDataSource.<anonymous> (/Users/jk/Projects/@serlo/api.serlo.org/node_modules/apollo-datasource-rest/src/RESTDataSource.ts:135:15)",
            "    at Generator.next (<anonymous>)",
            "    at /Users/jk/Projects/@serlo/api.serlo.org/node_modules/apollo-datasource-rest/dist/RESTDataSource.js:8:71",
            "    at new Promise (<anonymous>)",
            "    at __awaiter (/Users/jk/Projects/@serlo/api.serlo.org/node_modules/apollo-datasource-rest/dist/RESTDataSource.js:4:12)",
            "    at SerloDataSource.errorFromResponse (/Users/jk/Projects/@serlo/api.serlo.org/node_modules/apollo-datasource-rest/dist/RESTDataSource.js:75:16)",
            "    at SerloDataSource.<anonymous> (/Users/jk/Projects/@serlo/api.serlo.org/node_modules/apollo-datasource-rest/src/RESTDataSource.ts:102:24)",
            "    at Generator.next (<anonymous>)",
            "    at /Users/jk/Projects/@serlo/api.serlo.org/node_modules/apollo-datasource-rest/dist/RESTDataSource.js:8:71",
            "    at new Promise (<anonymous>)"
          ]
        }
      }
    }
  ],
  "data": {
    "uuid": null
  }
}

Expose TypeScript types as npm package

As a consumer of the API (e.g. frontend), it would be helpful to have access to the TypeScript types (that we mostly already have). Need to make sure, that:

  • Names are consistent.
  • The types actually match the return type (i.e. especially check that the subtypes are correct).
    Also:
  • CI that releases @serlo/api with same version number as the docker image.
  • Possibly include Edtr.io TypeScript tooling to easier check for breaking changes in the API.

Add mutation setUuidState

Similarly to https://github.com/serlo/api.serlo.org/blob/master/src/graphql/schema/notification/resolvers.ts#L77, we need a mutation

mutation setUuidState(id: Int!, trashed: Boolean!): AbstractUuid

that sets the trashed property of the corresponding uuid.

☂️ Umbrella Issue: Threads

Status

  • Queries are done 🎉
  • Mutations are tracked here: #111 and #177

We want to add threads to the uuid type. So no additional root query necessary. Something like:

{
  uuid(id: 1855) {
    threads {
      totalCount
      nodes {
        id
        createdAt
        updatedAt
        title
        archived
        trashed
        object # Back link to the uuid the thread is tied to
        comments {
          totalCount
          nodes {
            id
            content
            createdAt
            updatedAt
            author
          }
        }
      }
    }
  }
}

Furthermore, we need mutations for various types of actions:

mutation createThread(
  # ...
}
# createComment
# archiveThread
# ...

Required legacy API endpoints

  • GET /api/threads/:object-id returns all the threads for the object with the given id (with thread ids only)
  • GET /api/thread/:id returns the thread with the given id (including its comments)
  • POST /api/create-thread creates a thread
  • ...

Alias polyfills

  • uuid should be able to resolve /:id to the specific uuid.
  • User's alias should be /user/profile/:username.
  • uuid should be able to resolve /user/profile/:id to the specific user.
  • uuid should be abe to resolve /user/profile/:username to the specific user (might need a mapping from username to id).

Mutation _updateCache

Second part of #41.

We need a mutation

mutation _updateCache(keys: [String!]!)

that tells the API to update the cache of the given keys. To achieve this, we need to map the cache key to the corresponding data source (i.e. *.serlo.org* maps to SerloDataSource, spreadsheet-* maps to GoogleSheetApi) and then tells it to update the cache (i.e. executing the corresponding get method without returning early when the cache is already set).

  • Add mutation _updateCache.
  • Add unit test for *.serlo.org* cacheKey.
  • Add unit test for spreadsheet-* cacheKey.

☂️ Umbrella Issue: API

Approach for extending the API:

  • Add contract tests that define newly needed endpoints
  • Implement the GraphQL resolvers
  • Publish contract tests and add the new endpoints to https://github.com/serlo/serlo.org

Things to consider:

  • The new endpoints in https://github.com/serlo/serlo.org should be prefixed with /api (e.g. /api/license/<id>. Prefer GET requests where possible (i.e. prefer URL parameters over POST body)
  • Limit the required requests (e.g. fetch nested data that requires new requests only if it is requested)

Next Priorities:

Status:

Review playground token handling

Currently, https://api.serlo.org/___graphql goes through the Cloudflare Worker anyways, so there is no need for a specific playground service token. We could remove that and let the local playground use the (mocked) Cloudflare Worker secret instead.

add documentation

Would be nice if we can add some documentation to the GraphQL schema since the nomenclature in serlo.org is not always obvious :)

☂️ Umbrella Issue: Worker that updates cache

  • Query _getCacheKeys that returns a connection of strings that should be cached (#42).
  • Mutation _updateCache(keys: [String!]!) that tells api.serlo.org to update the cache of the corresponding keys (#51).
  • Docker image that uses _getCacheKeys and _updateCache to update/fill the cache.

Review alias handling

We allow special characters to be part of the url. We should probably url-encode these at the API level.

Deprecate _set* mutations in favor of _setCache

For each type, we have a _set* mutation that allows serlo.org to set the cache. This is kind of annoying to maintain. Moreover, the GraphQL typing doesn't help us in this place, anyways. So we should just get rid of it and combine a setCache mutation that acceps the key and the value.

☂️ Umbrella Issue: Documentation

IMHO, the documentation directly offered by GraphQL wasn't really helpful (see #7 and its implementation):

  • It made the code base less readable because of comments above every field.
  • We couldn't really document stuff that is not obvious from the names already (e.g. a seemingly arbitrary JSON-String).

Instead, we should add a human-created documentation that can also explain high-level stuff, e.g.:

  • a GitHub wiki in this repository
  • a statically generated site deployed via Vercel on docs.api.serlo.org
  • a docs site expanding projects on serlo.dev / docs.serlo.org that we can use in the future for other projects (currently it would be nice for content API & API). Might also be a nice place for changelogs etc.

API: add taxonomy

Needs some thinking before implementing. Basically we have two use cases:

  • When accessing an entity/page over uuid, we want to be able to access the taxonomy path to that entity/page for the breadcrumbs
  • We want to be able to access taxonomy uuids over uuid

Probably implement that similar to currentRevision:

  • Add taxonomy stuff to uuid
  • When accessing an entity/page over uuid and requesting taxonomyParent (name tbd), do sub-requests to uuid if needed. Something like
{
  uuid(id: 1855) {
    ... on Article {
      taxonomyPath: { # List of taxonomy elements, probably has sub types
        id
        title
      }
    }
  }
}

No app specific super class to data sources

At the moment, the data sources classes share only the super class RestDataSource without any super class and/or interface that is app specific. When the data sources scale we may have maintainability problems. Besides that, it can lead to verbose code when we have to use such classes in the same logic (v.g. resolver for updateCache, see #57 ).
CacheableDataSource would be a good name for super class/interface here.

Review peerDependencies

There are some unresolved / wrong peerDependencies. Although those doesn't seem to cause errors in the running, we should fix them were possible.

☂️ Umbrella Issue: Notifications

We want to expose the notifications of the currently authenticated user via our API. Since every notification has an event associated with it, this basically needs two components:

  • Expose the notifications.
  • Expose the events (This is the tricky part since there are a couple of different event types with different payloads).

In the end, we should have a notifications query of the following form:

{
  notifications() { # We might add filters at a later point
    totalCount      # The total number of notifications. Probably fixed at around ~20 right now in the legacy system
    nodes {         # The actual list of notifications
      id            # ID of the notifcation. We probably want to expose that as a string to be compatible with what we want to achieve in the future
      unread        # True iff the notification has not been read by the user.
      event {       # The event that triggered the notification.
        id          # ID of the event. Again, a string.
        type        # Type of the event. See a list of available types below.
        instance    # The instance the event has been triggered in.
        date        # Datetime the event has been created.
        actor {     # The user that triggered the event.
          # User subtype
        }
        object {    # The object that the event has been triggered on.
          # Uuid subtype
        }
        payload     # Arbitrary JSON-String containing additional information about the event. We should document that somewhere (per event type). 
      }
    }
  }
}

Furthermore, there should be a way to mark notifications as unread/read.

mutation setNotificationState(
  id     # The ID of the notification which read status should be changed.
  unread # The value the unread state should be set to.
)

Note: Both query and mutation are tied to the currently authenticated user. So they need to work with the user token.

Reference

Event types

These are all the event types that we currently support in the legacy system. We might want to rename that on the API level so that we have a consistent naming scheme in the future.

discussion/comment/archive
discussion/comment/create
discussion/create
discussion/restore
entity/create
entity/link/create
entity/link/remove
entity/revision/add
entity/revision/checkout
entity/revision/reject
license/object/set
taxonomy/term/associate
taxonomy/term/create
taxonomy/term/dissociate
taxonomy/term/parent/change
taxonomy/term/update
uuid/restore
uuid/trash

Required legacy API endpoints

  • GET /api/notifcations/:user-id returns all the notifications for the user with the given id (with event id only)
  • GET /api/event/:id returns the event with the given id
  • POST /api/set-notification-state/:id sets the unread state for the notification of the given id

Review maximum request body size

Observing a couple of 413 that is probably the API server. We should increase the maximum request body size (at least for services like serlo.org since the legacy API requests may be quite large).

Add user-specific query subscriptions

To check which entities the current user is subscribed to. An example query would be:

{
  subscriptions(first: 5) {   # Returns a connection of Uuids
    totalCount
    nodes {
      __typename
      id
    }
  }
}

Basic approach:

  • Add subscriptions query in src/graphql/schema/subscriptions (user-specific, so similarly to src/graphql/schema/notifications)
  • Add unit tests for subscriptions that mocks the legacy endpoint GET /api/subscriptions/:user-id (that should return a list of ids)
  • Add contract test for subscriptions that uses the legacy endpoint GET /api/subscriptions/:user-id (similarly to _cacheKeys)

Add cache worker docker image

Third part of #41.

Basically:

  • We need a function that uses _cacheKeys and _updateCache to fill/update the whole cache:
    • Probably fine to create a new file src/worker.ts for that (independent of the current server) for now. Might refactor the repo into a monorepo. Will see.
    • Accepts the API endpoint as a function argument (and possibly the service name + secret for the token)
    • Uses graphql-request to do the requests. Probably something like:
      • Fetch the next 10 keys using _cacheKeys , update those keys using _updateCache, continue until no cache keys left.
      • Shouldn't fail if updating a single cache key fails for some reason.
    • Can probably be unit tested.
  • After that: create a Docker image that executes src/worker.ts (using the provided environment variables for arguments). As noted above: we might want to refactor this repo into a monorepo for that since the worker will have different dependencies compared to the server.
    • @inyono Also add to CI and stuff.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.