Khan documentation¶
Contents:
Overview¶
What is Khan? Khan is an HTTP “resty” API for managing clans for games. It could be used to manage groups of people, but our aim is players in a game.
Khan allows your app to focus on the interaction required to creating clans and managing applications, instead of the backend required for actually doing it.
Features¶
- Multi-tenant - Khan already works for as many games as you need, just keep adding new games;
- Clan Management - Create and manage clans, their metadata as well as promote and demote people in their rosters;
- Player Management - Manage players and their metadata, as well as their applications to clans;
- Applications - Khan handles the work involved with applying to clans, inviting people to clans, accepting, denying and kicking;
- Clan Search - Search a list of clans to present your player with relevant options;
- Top Clans - Choose from a specific dimension to return a list of the top clans in that specific range (SOON);
- Web Hooks - Need to integrate your clan system with another application? We got your back! Use our web hooks sytem and plug into whatever events you need;
- Auditing Trail - Track every action coming from your games (SOON);
- New Relic Support - Natively support new relic with segments in each API route for easy detection of bottlenecks;
- Easy to deploy - Khan comes with containers already exported to docker hub for every single of our successful builds. Just pick your choice!
Architecture¶
Khan is based on the premise that you have a backend server for your game. That means we do not employ any means of authentication.
There’s no validation if the actions you are performing are valid as well. We have TONS of validation around the operations themselves being valid.
What we don’t have are validations that test whether the source of the request can perform the request (remember the authentication bit?).
Khan also offers a JSON metadata
field in its Player and Clan models. This means that your game can store relevant information that can later be used to sort players, for example.
The Stack¶
For the devs out there, our code is in Go, but more specifically:
Who’s Using it¶
Well, right now, only us at TFG Co, are using it, but it would be great to get a community around the project. Hope to hear from you guys soon!
How To Contribute?¶
Just the usual: Fork, Hack, Pull Request. Rinse and Repeat. Also don’t forget to include tests and docs (we are very fond of both).
Installing Khan¶
TBW.
Hosting Khan¶
There are three ways to host Khan: docker, binaries or from source.
Docker¶
Running Khan with docker is rather simple. Our docker container image comes bundled with the API binary. All you need to do is load balance all the containers and you’re good to go. The API runs at port 8080
in the docker image.
Khan uses PostgreSQL to store clans information. The container takes environment variables to specify this connection:
KHAN_POSTGRES_HOST
- PostgreSQL host to connect to;KHAN_POSTGRES_PORT
- PostgreSQL port to connect to;KHAN_POSTGRES_USER
- Password of the PostgreSQL Server to connect to;KHAN_POSTGRES_DBNAME
- Database name of the PostgreSQL Server to connect to;KHAN_POSTGRES_SSLMODE
- SSL Mode to connect to postgres with;
Other than that, there are a couple more configurations you can pass using environment variables:
KHAN_NEWRELIC_KEY
- If you have a New Relic account, you can use this variable to specify your API Key to populate data with New Relic API;KHAN_SENTRY_URL
- If you have a sentry server you can use this variable to specify your project’s URL to send errors to;KHAN_EXTENSIONS_DOGSTATSD_HOST
- If you have a statsd datadog daemon, Podium will publish metrics to the given host at a certain port. Ex. localhost:8125;KHAN_EXTENSIONS_DOGSTATSD_RATE
- If you have a statsd daemon, Podium will export metrics to the deamon at the given rate;KHAN_EXTENSIONS_DOGSTATSD_TAGS_PREFIX
- If you have a statsd daemon, you may set a prefix to every tag sent to the daemon;
If you want to expose Khan outside your internal network it’s advised to use Basic Authentication. You can specify basic authentication parameters with the following environment variables:
KHAN_BASICAUTH_USERNAME
- If you specify this key, Khan will be configured to use basic auth with this user;KHAN_BASICAUTH_PASSWORD
- If you specifyBASICAUTH_USERNAME
, Khan will be configured to use basic auth with this password;
Example command for running with Docker¶
$ docker pull tfgco/khan
$ docker run -t --rm -e "KHAN_POSTGRES_HOST=<postgres host>" -e "KHAN_POSTGRES_PORT=<postgres port>" -p 8080:80 tfgco/khan
In order to run Khan’s workers using docker you just need to send the KHAN_RUN_WORKER
environment variable as true
.
Example command for running workers with Docker¶
$ docker pull tfgco/khan
$ docker run -t --rm -e "KHAN_POSTGRES_HOST=<postgres host>" -e "KHAN_POSTGRES_PORT=<postgres port>" -e "KHAN_RUN_WORKERS=true" -p 9999:80 tfgco/khan
Binaries¶
Whenever we publish a new version of Khan, we’ll always supply binaries for both Linux and Darwin, on i386 and x86_64 architectures. If you’d rather run your own servers instead of containers, just use the binaries that match your platform and architecture.
The API server is the khan
binary. It takes a configuration yaml file that specifies the connection to PostgreSQL and some additional parameters. You can learn more about it at default.yaml.
The workers can be started using the same khan
binary. It takes a configuration yaml file that specifies the connection to PostgreSQL and some additional parameters. You can learn more about it at default.yaml.
Source¶
Left as an exercise to the reader.
Game Configuration¶
Being a multi-tenant clan server, Khan allows for many different configurations per tenant. Each tenant is a different game and is identified by it’s game ID.
Before any clan operation can be performed, you must create a game in Khan. The good news here is that creating/updating games are idempotent operations. You can keep executing it any time your game changes. That’s ideal to be executed in a deploy script, for instance.
Creating/Updating a Game¶
We recommend that the Update
operation of the Game resource be used in detriment of the Create
one. The reasoning here is that the Ùpdate` operation is idempotent (you can run it as many times as you want with the same result). If your game does not exist yet, it will create it, otherwise just updated it with the new configurations.
To Create/Update your game, just do a PUT
request to http://my-khan-server/games/my-game-public-id
, where my-game-public-id
is the ID you’ll be using for all your game’s operations in the future. The payload for the request is a JSON object in the body and should be as follows:
{
"name": [string],
"metadata": [JSON],
"membershipLevels": [JSON],
"minLevelToAcceptApplication": [int],
"minLevelToCreateInvitation": [int],
"minLevelToRemoveMember": [int],
"minLevelOffsetToPromoteMember": [int],
"minLevelOffsetToRemoveMember": [int],
"minLevelOffsetToDemoteMember": [int],
"maxMembers": [int],
"maxClansPerPlayer": [int],
"cooldownAfterDeny": [int],
"cooldownAfterDelete": [int],
"cooldownBeforeInvite": [int],
"cooldownBeforeApply": [int],
"maxPendingInvites": [int],
"clanHookFieldsWhitelist": [string],
"playerHookFieldsWhitelist": [string],
}
If the operation is successful, you’ll receive a JSON object saying it succeeded:
{
"success": true
}
Game Configuration Settings¶
As can be seen from the previous section, there are a lot of different configurations you can do per game. These will be thoroughly explained in this section.
name¶
The name of your game. This is used mainly for easier reasoning of what this game is when debugging.
Type: string
Sample Value: My Sample Game
metadata¶
Metadata related to your clan. This is a JSON object and can store anything you need to. Each game will probably have a different usage for this attribute: clan nationality, clan flag image URL, number of victories for the clan to date, etc.
This value is a black box as far as Khan is concerned. It’s not used to decide any rules for clan management.
Type: JSON
Sample Value: { "country": "BR", "language": "pt-BR" }
membershipLevels¶
The available membership levels the specified game supports. This is a way to specify the hierarchy between members in your game’s clans. These levels are used in other configuration settings like minLevelToRemoveMember
or minLevelOffsetToPromoteMember
, among others.
The membership values (integer) should grow in importance, with the highest number being the highest member level.
Type: JSON
Sample Value: { "member": 1, "leader": 2, "owner": 3 }
minLevelToAcceptApplication¶
The minimum member level (as specified in the membershipLevels configuration) required to accept a pending application to the clan.
Type: integer
Sample Value: 2
minLevelToCreateInvitation¶
The minimum member level (as specified in the membershipLevels configuration) required to invite someone into a clan.
Type: integer
Sample Value: 2
minLevelToRemoveMember¶
The minimum member level (as specified in the membershipLevels configuration) required to remove someone from the clan.
Type: integer
Sample Value: 2
minLevelOffsetToRemoveMember¶
This configuration specifies the required difference in level between a player and the player being removed from the clan.
Let’s look at an example to make things easier:
John has a membership level of 3, Paul has a membership level of 2 and
Ted has a membership level of 1.
If the clan has a minLevelOffsetToRemoveMember of 2, that means that only
John can remove Ted, but if that configuration is 1, then both John and Paul
can remove Ted.
Type: integer
Sample Value: 2
minLevelOffsetToPromoteMember¶
This configuration specifies the required difference in level between a player and the player being promoted (calculated BEFORE the promotion).
What this means is that a player can only promote another player if that player is minLevelOffsetToPromoteMember
levels below their own level before the promotion takes place.
If the minLevelOffsetToPromoteMember
is greater than 1, then only the clan owner can promote someone to the highest available level(s).
Let’s look at an example to make things easier:
John has a membership level of 5, Paul has a membership level of 3 and
Ted has a membership level of 1.
If the clan has a minLevelOffsetToPromoteMember of 2, that means that only
John can promote Ted up to level 4, but if that configuration is 1,
then both John and Paul can promote Ted (Paul can promote Ted to level 3).
Type: integer
Sample Value: 2
minLevelOffsetToDemoteMember¶
This configuration specifies the required difference in level between a player and the player being demoted (calculated BEFORE the demotion).
If the minLevelOffsetToDemoteMember
is greater than 1, then only the clan owner can demote someone from the highest available level(s).
Let’s look at an example to make things easier:
John has a membership level of 5, Paul has a membership level of 4 and
Ted has a membership level of 3.
If the clan has a minLevelOffsetToDemoteMember of 2, that means that only
John can demote Ted, but if that configuration is 1, then both John and
Paul can demote Ted.
Type: integer
Sample Value: 2
maxMembers¶
This configuration specifies the maximum number of members a clan can have.
Type: integer
Sample Value: 50
maxClansPerPlayer¶
This configuration specifies the maximum number of clans a player can be a member of.
Type: integer
Sample Value: 1
cooldownAfterDeny¶
Time (in seconds) the player must wait before applying/being invited to a new membership after the last membership application/invite was denied.
Type: integer
Sample Value: 360
cooldownAfterDelete¶
Time (in seconds) the player must wait before applying/being invited to a new membership after the last membership application/invite was deleted.
Type: integer
Sample Value: 720
cooldownBeforeInvite¶
Time (in seconds) a clan member must wait before inviting a member to a new membership after the last membership application/invite was created.
Type: integer
Sample Value: 720
cooldownBeforeApply¶
Time (in seconds) a player must wait before applying for a clan after the last membership application/invite was created.
Type: integer
Sample Value: 480
maxPendingInvites¶
Maximum number of pending invites each player can have withstanding. Set this value to -1
if your game has no limits on maximum pending invites.
Type: integer
Sample Value: 20
clanHookFieldsWhitelist¶
A comma-separated-values list of properties in the clan’s metadata that will trigger the Clan Updated hook upon change.
Type: string
Sample Value: trophies,country
playerHookFieldsWhitelist¶
A comma-separated-values list of properties in the player’s metadata that will trigger the Player Updated hook upon change.
Type: string
Sample Value: trophies,country
Using Web Hooks¶
You can use khan’s web hooks to send detailed event information to other servers. The use cases for this are plenty, since the need for integrating micro-services is very common.
One possible use case would be to notify the chat channel for your game with information about people joining/leaving the clan or for new applications.
Webhook Workers¶
Khan uses GoWorkers workers to send webhooks. This means that you need to run the khan worker
command to start as many workers as you want.
Webhook Specific Configuration¶
Khan has some configuration entries specific to webhooks:
redis.host
- Redis server host used for GoWorkers;redis.port
- Redis server port used for GoWorkers;redis.database
- Redis server database used for GoWorkers;redis.pool
- Redis connection pool size used for GoWorkers;redis.password
- Redis password used for GoWorkers;webhooks.timeout
- Timeout for webhook HTTP connections;webhooks.workers
- Number of GoWorkers to start with each instance of Khan worker;webhooks.runStats
- Will the GoWorkers stats server run in each Khan worker instance?;webhooks.statsPort
- Port that the stats server of GoWorkers will run in.
Registering a Web Hook¶
Registering a web hook is done using the Create Web Hook Route. A hook can also be removed using the Remove Web Hook Route. Just make sure you keep the PublicID that was returned by the Create Hook route as it is required to remove a hook.
How do Web Hooks work?¶
When an event happen in Khan, it will look for all the hooks registered for the game that the event happened in.
It will then do a POST request to all of them with the payload relevant to the event (more about the payloads in the Events section). The payload for the events is just a JSON object in the body of the request.
Let’s say we want to keep track of all the clans that are created in our game. We could create a web hook for the ClanCreated
event and Khan would call our hook with this payload:
{
gameID: "my-game",
publicID: "clan-public-id",
name: "My Fancy Clan"
}
We could then use this information to store this clan in our Database, to integrate with a chat channel, to provision some third-party system for clans, etc.
URL Format and Flexibility¶
When registering a new URL, Khan allows you to specify the URL as a Template.
Let’s assume you need to use the clan public ID in your URL to store a newly created clan at http://my-system.com:3030/clans/some-clan/
. Instead of some-clan, we need to use the clan’s public ID.
This is very easy to do with Khan. You can interpolate any of the keys in the payload using {{key}}
. Bear in mind that only two types of keys can be used:
- top-level keys - if you use a key that’s in the first level of depth in the payload, you are good with any type of key;
- dot separated keys - this type of key will only work if all the keys in the path are objects (except the last one).
Let’s try with the payload for the player created event:
{
"success": true,
"gameID": "some-game",
"publicID": "playerPublicID",
"name": "Player Name",
"metadata": {
"score": 1200,
"league": {
ranking: "diamond",
position: 30
}
}
}
Now imagine we want the player to be included in the league he belongs to. We could use an URL like http://my-server.com:3030/players/{{publicID}}/leagues/{{metadata.league.ranking}}/
. This would be translated by Khan to http://my-server.com:3030/players/playerPublicID/leagues/diamond/
.
Event Types¶
So what types of events can you create Web Hooks for?
Game Hooks¶
Game Updated¶
Event Type: 0
Payload:
{
"success": true,
"type": 0, // Event Type
"publicID": [string], // Game ID
"name": [string], // Game Name
"metadata": [JSON], // JSON Object containing game metadata.
"membershipLevels": [JSON], // JSON Object mapping membership levels
"minLevelToAcceptApplication": [int], // Minimum level of membership required
// to accept players into clan
"minLevelToCreateInvitation": [int], // Minimum level of membership required
// to invite players into clan
"minLevelToRemoveMember": [int], // Minimum level of membership required
// to remove players from clan
"minLevelOffsetToRemoveMember": [int], // A player must be at least this offset
// higher than the player being removed.
"minLevelOffsetToPromoteMember": [int], // A player must be at least this offset
// higher than the player being promoted
"minLevelOffsetToDemoteMember": [int], // A player must be at least this offset
// higher than the player being demoted
"maxMembers": [int], // Maximum number of players in the clan
"maxClansPerPlayer": [int] // Maximum number of clans the player can be
// member of
}
Player Hooks¶
Player Created¶
Event Type: 1
Payload:
{
"gameID": [string], // Game ID
"type": 1, // Event Type
"publicID": [string], // Created Player PublicID This id should
// be used when referring to the player in
// future operations.
"name": [string], // Player Name
"metadata": [JSON], // JSON Object containing player metadata
"membershipCount": [int], // Number of clans this player is a member of
"ownershipCount": [int], // Number of clans this player is an owner of
"id": [UUID], // unique id that identifies the hook
"timestamp": [timestamp] // timestamp in the RFC3339 format
}
Player Updated¶
Event Type: 2
Payload:
{
"gameID": [string], // Game ID
"type": 2, // Event Type
"publicID": [string], // Created Player PublicID This id should
// be used when referring to the player in
// future operations.
"name": [string], // Player Name
"metadata": [JSON], // JSON Object containing player metadata
"membershipCount": [int], // Number of clans this player is a member of
"ownershipCount": [int], // Number of clans this player is an owner of
"id": [UUID], // unique id that identifies the hook
"timestamp": [timestamp] // timestamp in the RFC3339 format
}
Clan Hooks¶
Clan Created¶
Event Type: 3
Payload:
{
"gameID": [string], // Game ID
"type": 3, // Event Type
"clan": {
"publicID": [string], // Created Clan PublicID This id should
// be used when referring to the clan in
// future operations.
"name": [string], // Clan Name
"metadata": [JSON], // JSON Object containing clan's metadata
"allowApplication": [bool], // Indicates whether this clan acceps applications
"autoJoin": [bool] // Indicates whether this clan automatically
// accepts applications
}
"id": [UUID], // unique id that identifies the hook
"timestamp": [timestamp] // timestamp in the RFC3339 format
}
Clan Updated¶
Event Type: 4
Payload:
{
"gameID": [string], // Game ID
"type": 4, // Event Type
"clan": {
"publicID": [string], // Updated Clan PublicID This id should
// be used when referring to the clan in
// future operations.
"name": [string], // Clan Name
"metadata": [JSON], // JSON Object containing clan's metadata
"allowApplication": [bool], // Indicates whether this clan acceps applications
"autoJoin": [bool] // Indicates whether this clan automatically
// accepts applications
}
"id": [UUID], // unique id that identifies the hook
"timestamp": [timestamp] // timestamp in the RFC3339 format
}
WARNING: This event may be skipped if the game’s clanHookFieldsWhitelist
field is not empty and none of the fields that actually changed is in the whitelist. Imagine you have a document like this:
{
"trophies": 30,
"country": "US",
}
The game is configured with clanHookFieldsWhitelist = "country"
. That means that changing the number of trophies won’t dispatch hooks, but changing the country (or any other clan property) will.
Clan Owner Left¶
Event Type: 5
Payload:
{
"gameID": [string],
"type": 5, // Event Type
"isDeleted": [bool], //Indicates whether the clan was deleted
//because there were no members left
"clan": {
"publicID": [string], // Updated Clan PublicID
"name": [string], // Clan Name
"metadata": [JSON], // JSON Object containing clan's metadata
"allowApplication": [bool] // Indicates whether this clan acceps applications
"autoJoin": [bool], // Indicates whether this clan automatically
// accepts applications
"membershipCount": [int], // Number of members in clan
},
"previousOwner": { // The owner that left
"publicID": [string], // Previous Owner PublicID
"name": [string], // Player Name
"metadata": [JSON], // JSON Object containing player metadata
"membershipCount": [int], // Number of clans this player is a member of
"ownershipCount": [int] // Number of clans this player is an owner of
},
"newOwner": { // After the owner left, this is the new owner
"publicID": [string], // New Owner PublicID
"name": [string], // Player Name
"metadata": [JSON], // JSON Object containing player metadata
"membershipCount": [int], // Number of clans this player is a member of
"ownershipCount": [int] // Number of clans this player is an owner of
},
"id": [UUID], // unique id that identifies the hook
"timestamp": [timestamp] // timestamp in the RFC3339 format
}
Clan Ownership Transferred to Another Player¶
Event Type: 6
Payload:
{
"gameID": [string],
"type": 6, // Event Type
"clan": {
"publicID": [string], // Updated Clan PublicID
"name": [string], // Clan Name
"metadata": [JSON], // JSON Object containing clan's metadata
"allowApplication": [bool] // Indicates whether this clan acceps applications
"autoJoin": [bool], // Indicates whether this clan automatically
// accepts applications
"membershipCount": [int], // Number of members in clan
},
"previousOwner": { // The previous owner
"publicID": [string], // Previous Owner PublicID
"name": [string], // Player Name
"metadata": [JSON], // JSON Object containing player metadata
"membershipCount": [int], // Number of clans this player is a member of
"ownershipCount": [int] // Number of clans this player is an owner of
},
"newOwner": { // Player that the owner transferred ownership to
"publicID": [string], // New Owner PublicID
"name": [string], // Player Name
"metadata": [JSON], // JSON Object containing player metadata
"membershipCount": [int], // Number of clans this player is a member of
"ownershipCount": [int] // Number of clans this player is an owner of
},
"id": [UUID], // unique id that identifies the hook
"timestamp": [timestamp] // timestamp in the RFC3339 format
}
Membership Hooks¶
Membership Created¶
This event occurs if a player applies to a clan (player == requestor) or if a player is invited to a clan (player != requestor).
Event Type: 7
Payload:
{
"gameID": [string],
"type": 7, // Event Type
"clan": {
"publicID": [string], // Clan that player is applying to
"name": [string], // Clan Name
"metadata": [JSON], // JSON Object containing clan's metadata
"allowApplication": [bool] // Indicates whether this clan acceps applications
"autoJoin": [bool], // Indicates whether this clan automatically
// accepts applications
"membershipCount": [int], // Number of members in clan
},
"player": { // Player that is applying/being invited to the clan
"publicID": [string], // Applicant PublicID
"name": [string], // Player Name
"metadata": [JSON], // JSON Object containing player metadata
"membershipCount": [int], // Number of clans this player is a member of
"ownershipCount": [int], // Number of clans this player is an owner of
"membershipLevel": [string] // The level of the player's membership
},
"requestor": { // Player that requested this membership application/invite
"publicID": [string], // Requestor PublicID
"name": [string], // Player Name
"metadata": [JSON], // JSON Object containing player metadata
"membershipCount": [int], // Number of clans this player is a member of
"ownershipCount": [int] // Number of clans this player is an owner of
},
"id": [UUID], // unique id that identifies the hook
"timestamp": [timestamp] // timestamp in the RFC3339 format
}
Membership Approved¶
This event occurs if an application or an invite to a clan gets approved. If the membership that was approved was an invitation into the clan, the requestor and the player will be the same Player. Otherwise, the requestor will be whomever approved the membership.
Event Type: 8
Payload:
{
"gameID": [string],
"type": 8, // Event Type
"clan": {
"publicID": [string], // Clan that membership was approved
"name": [string], // Clan Name
"metadata": [JSON], // JSON Object containing clan's metadata
"allowApplication": [bool] // Indicates whether this clan acceps applications
"autoJoin": [bool], // Indicates whether this clan automatically
// accepts applications
"membershipCount": [int], // Number of members in clan
},
"player": { // Player that was approved into the clan
"publicID": [string], // Player PublicID
"name": [string], // Player Name
"metadata": [JSON], // JSON Object containing player metadata
"membershipCount": [int], // Number of clans this player is a member of
"ownershipCount": [int], // Number of clans this player is an owner of
"membershipLevel": [string] // The level of the player's membership
},
"requestor": { // Player that approved the membership
"publicID": [string], // Requestor PublicID
"name": [string], // Player Name
"metadata": [JSON], // JSON Object containing player metadata
"membershipCount": [int], // Number of clans this player is a member of
"ownershipCount": [int] // Number of clans this player is an owner of
},
"creator": { // Player that created the membership (invited or applied)
"publicID": [string], // Creator PublicID
"name": [string], // Creator Name
"metadata": [JSON], // JSON Object containing creator metadata
"membershipCount": [int], // Number of clans this creator is a member of
"ownershipCount": [int] // Number of clans this creator is an owner of
},
"id": [UUID], // unique id that identifies the hook
"timestamp": [timestamp] // timestamp in the RFC3339 format
}
Membership Denied¶
This event occurs if an application or an invite to a clan gets denied. If the membership that was denied was an invitation into the clan, the requestor and the player will be the same Player. Otherwise, the requestor will be whomever denied the membership.
Event Type: 9
Payload:
{
"gameID": [string],
"type": 9, // Event Type
"clan": {
"publicID": [string], // Clan that membership was denied
"name": [string], // Clan Name
"metadata": [JSON], // JSON Object containing clan's metadata
"allowApplication": [bool] // Indicates whether this clan acceps applications
"autoJoin": [bool], // Indicates whether this clan automatically
// accepts applications
"membershipCount": [int], // Number of members in clan
},
"player": { // Player that was denied into the clan
"publicID": [string], // Player PublicID
"name": [string], // Player Name
"metadata": [JSON], // JSON Object containing player metadata
"membershipCount": [int], // Number of clans this player is a member of
"ownershipCount": [int], // Number of clans this player is an owner of
"membershipLevel": [string] // The level of the player's membership
},
"requestor": { // Player that denied the membership
"publicID": [string], // Requestor PublicID
"name": [string], // Player Name
"metadata": [JSON], // JSON Object containing player metadata
"membershipCount": [int], // Number of clans this player is a member of
"ownershipCount": [int] // Number of clans this player is an owner of
},
"creator": { // Player that created the membership (invited or applied)
"publicID": [string], // Creator PublicID
"name": [string], // Creator Name
"metadata": [JSON], // JSON Object containing creator metadata
"membershipCount": [int], // Number of clans this creator is a member of
"ownershipCount": [int] // Number of clans this creator is an owner of
},
"id": [UUID], // unique id that identifies the hook
"timestamp": [timestamp] // timestamp in the RFC3339 format
}
Member Promoted¶
Event Type: 10
Payload:
{
"gameID": [string],
"type": 10, // Event Type
"clan": {
"publicID": [string], // Clan that member was promoted
"name": [string], // Clan Name
"metadata": [JSON], // JSON Object containing clan's metadata
"allowApplication": [bool] // Indicates whether this clan acceps applications
"autoJoin": [bool], // Indicates whether this clan automatically
// accepts applications
"membershipCount": [int], // Number of members in clan
},
"player": { // Player that was promoted
"publicID": [string], // Player PublicID
"name": [string], // Player Name
"metadata": [JSON], // JSON Object containing player metadata
"membershipCount": [int], // Number of clans this player is a member of
"ownershipCount": [int], // Number of clans this player is an owner of
"membershipLevel": [string] // The new level of the player's membership
},
"requestor": { // Player that promoted this member
"publicID": [string], // Requestor PublicID
"name": [string], // Player Name
"metadata": [JSON], // JSON Object containing player metadata
"membershipCount": [int], // Number of clans this player is a member of
"ownershipCount": [int] // Number of clans this player is an owner of
},
"id": [UUID], // unique id that identifies the hook
"timestamp": [timestamp] // timestamp in the RFC3339 format
}
Member Demoted¶
Event Type: 11
Payload:
{
"gameID": [string],
"type": 11, // Event Type
"clan": {
"publicID": [string], // Clan that member was demoted
"name": [string], // Clan Name
"metadata": [JSON], // JSON Object containing clan's metadata
"allowApplication": [bool] // Indicates whether this clan acceps applications
"autoJoin": [bool], // Indicates whether this clan automatically
// accepts applications
"membershipCount": [int], // Number of members in clan
},
"player": { // Player that was demoted
"publicID": [string], // Player PublicID
"name": [string], // Player Name
"metadata": [JSON], // JSON Object containing player metadata
"membershipCount": [int], // Number of clans this player is a member of
"ownershipCount": [int] // Number of clans this player is an owner of,
"membershipLevel": [string] // The new level of the player's membership
},
"requestor": { // Player that demoted this member
"publicID": [string], // Requestor PublicID
"name": [string], // Player Name
"metadata": [JSON], // JSON Object containing player metadata
"membershipCount": [int], // Number of clans this player is a member of
"ownershipCount": [int] // Number of clans this player is an owner of
},
"id": [UUID], // unique id that identifies the hook
"timestamp": [timestamp] // timestamp in the RFC3339 format
}
Member Left¶
Event Type: 12
Payload:
{
"gameID": [string],
"type": 12, // Event Type
"clan": {
"publicID": [string], // Clan that member left
"name": [string], // Clan Name
"metadata": [JSON], // JSON Object containing clan's metadata
"allowApplication": [bool] // Indicates whether this clan acceps applications
"autoJoin": [bool], // Indicates whether this clan automatically
// accepts applications
"membershipCount": [int], // Number of members in clan
},
"player": { // Player that left
"publicID": [string], // Player PublicID
"name": [string], // Player Name
"metadata": [JSON], // JSON Object containing player metadata
"membershipCount": [int], // Number of clans this player is a member of
"ownershipCount": [int], // Number of clans this player is an owner of
"membershipLevel": [string] // The level of the player's membership
},
"requestor": { // Player that removed leaving player (if they left
// on their own, then this is the same as player)
"publicID": [string], // Requestor PublicID
"name": [string], // Player Name
"metadata": [JSON], // JSON Object containing player metadata
"membershipCount": [int], // Number of clans this player is a member of
"ownershipCount": [int] // Number of clans this player is an owner of
},
"id": [UUID], // unique id that identifies the hook
"timestamp": [timestamp] // timestamp in the RFC3339 format
}
Khan API¶
Healthcheck Routes¶
Healthcheck¶
GET /healthcheck
Validates that the app is still up, including the database connection.
Success Response
Code:
200
Content:
"WORKING"
Headers:
It will add an
KHAN-VERSION
header with the current khan module version.
Error Response
It will return an error if it failed to connect to the database.
Code:
500
Content:
"Error connecting to database: <error-details>"
Status Routes¶
Status¶
GET /status
Returns statistics on the health of khan.
Success Response
Code:
200
Content:
{ "app": { "errorRate": [float] // Exponentially Weighted Moving Average Error Rate }, "dispatch": { "pendingJobs": [int] // Pending hook jobs to be sent } }
Game Routes¶
Create Game¶
POST /games
Creates a new game with the given parameters.
Payload
{ "publicID": [string], // 36 characters max, must be unique "name": [string], // 2000 characters max "metadata": [JSON], "membershipLevels": [JSON], "minLevelToAcceptApplication": [int], "minLevelToCreateInvitation": [int], "minLevelToRemoveMember": [int], "minLevelOffsetToRemoveMember": [int], "minLevelOffsetToPromoteMember": [int], "minLevelOffsetToDemoteMember": [int], "maxMembers": [int], "maxClansPerPlayer": [int], "cooldownAfterDeny": [int], "cooldownAfterDelete": [int], "cooldownBeforeInvite": [int], "cooldownBeforeApply": [int], "maxPendingInvites": [int], "clanHookFieldsWhitelist": [string], "playerHookFieldsWhitelist": [string], }
Metadata is intended to include all game specific configuration that is not directly related to the khan’s clan management. For example, clank ranking, clan trophies, clan description, etc.
Some parameters require special attention:
membershipLevels: Should be a JSON mapping levels (strings) to integers:
{ "Member": 1, "Elder": 2, "CoLeader": 3 }
The integer part of the level will be used to compare with
minLevel...
andminLevelOffset...
when performing membership operations.minLevelToAcceptApplication: A member cannot accept a player’s application to join the clan unless their level is greater or equal to this parameter.
minLevelToCreateInvitation: A member cannot invite a player to join the clan unless their level is greater or equal to this parameter.
minLevelOffsetToRemoveMember: A member cannot remove another member unless their level is at least
MinLevelOffsetToRemoveMember
levels greater than the level of the member they wish to promote.minLevelOffsetToPromoteMember: A member cannot promote another member unless their level is at least
minLevelOffsetToPromoteMember
levels greater than the level of the member they wish to promote.minLevelOffsetToDemoteMember: A member cannot demote another member unless their level is at least
minLevelOffsetToDemoteMember
levels greater than the level of the member they wish to demote.maxMembers: Maximum number of members a clan of this game can have.
maxClansPerPlayer: Maximum number of clans a player can be member of.
cooldownAfterDeny: Time (in seconds) the player must wait before applying/being invited to a new membership after the last membership application/invite was denied.
cooldownAfterDelete: Time (in seconds) the player must wait before applying/being invited to a new membership after the last membership application/invite was deleted.
cooldownBeforeInvite: Time (in seconds) a clan member must wait before inviting a member to a new membership after the last membership application/invite was created.
cooldownBeforeApply: Time (in seconds) a player must wait before applying for a clan after the last membership application/invite was created.
maxPendingInvites: Maximum number of pending invites each player can have withstanding. Set this value to -1 if your game has no limits on maximum pending invites.
clanHookFieldsWhitelist: If you change metadata very frequently in clans, you can specify here the fields in your metadata document for which you’d like to have the clan updated hook triggered. If no fields are specified, the hook will be triggered in all updates. If you don’t want any metadata changes to trigger hooks, just set this to “none” or any key that does not exist in your metadata document.
playerHookFieldsWhitelist: If you change metadata very frequently in players, you can specify here the fields in your metadata document for which you’d like to have the player updated hook triggered. If no fields are specified, the hook will be triggered in all updates. If you don’t want any metadata changes to trigger hooks, just set this to “none” or any key that does not exist in your metadata document.
Success Response
Code:
200
Content:
{ "success": true, "publicID": [string] // game public id }
Error Response
It will return an error if an invalid payload is sent or if there are missing parameters.
Code:
400
Content:
{ "success": false, "reason": [string] }
It will return an error if there are invalid parameters.
Code:
422
Content:
{ "success": false, "reason": [string] }
Code:
500
Content:
{ "success": false, "reason": [string] }
Update Game¶
PUT /games/:gameID
Updates the game with that has publicID gameID
.
Payload
{ "name": [string], // 2000 characters max "metadata": [JSON], "membershipLevels": [JSON], "minLevelToAcceptApplication": [int], "minLevelToCreateInvitation": [int], "minLevelToRemoveMember": [int], "minLevelOffsetToPromoteMember": [int], "minLevelOffsetToDemoteMember": [int], "maxMembers": [int], "maxClansPerPlayer": [int], "cooldownAfterDeny": [int], "cooldownAfterDelete": [int], "cooldownBeforeInvite": [int], "cooldownBeforeApply": [int], "maxPendingInvites": [int], "clanHookFieldsWhitelist": [string], "playerHookFieldsWhitelist": [string] }
Success Response
Code:
200
Content:
{ "success": true }
Error Response
It will return an error if an invalid payload is sent or if there are missing parameters.
Code:
400
Content:
{ "success": false, "reason": [string] }
It will return an error if there are invalid parameters.
Code:
422
Content:
{ "success": false, "reason": [string] }
Code:
500
Content:
{ "success": false, "reason": [string] }
Hook Routes¶
More about web hooks can be found in Using WebHooks.
Supported Web Hook Event Types¶
0 Game Updated
- Happens when a game is updated;1 Player Created
- Happens when a new player is created;2 Player Updated
- Happens when an existing player is updated;3 Clan Created
- Happens when a new clan is created;4 Clan Updated
- Happens when an existing clan is updated;5 Clan Owner Left
- Happens when the owner of a clan leaves the clan without transferring the ownership to someone else;6 Ownership of the Clan transferred
- Happens when the owner of a clan transfers ownership to another player;7 Membership Created
- Happens when either someone applies to a clan or is invited;8 Membership Approved
- Happens when a pending membership to a clan is approved;9 Membership Denied
- Happens when a pending membership to a clan is denied;10 Member Promoted
- Happens when a member of the clan is promoted;11 Member Demoted
- Happens when a pending member of the clan is demoted;12 Member Left
- Happens when a member of the clan is either removed or leaves the clan.
Create Hook¶
POST /games/:gameID/hooks
Creates a new web hook for the specified game when the specified event type happens.
Payload
{ "type": [int], // Event Type "hookURL": [string] // the URL to call with the payload // for the specified event. }
Success Response
Code:
200
Content:
{ "success": true "publicID": [uuid] // This is the id required to remove the hook. // It should be stored with the client app. }
Error Response
It will return an error if an invalid payload is sent or if there are missing parameters.
Code:
400
Content:
{ "success": false, "reason": [string] }
Code:
500
Content:
{ "success": false, "reason": [string] }
Remove Hook¶
DELETE /games/:gameID/hooks/:hookPublicID
Removes a web hook created with the Create Hook route. No payload is required for this route.
Success Response
Code:
200
Content:
{ "success": true }
Error Response
Code:
500
Content:
{ "success": false, "reason": [string] }
Player Routes¶
Create Player¶
POST /games/:gameID/players
Creates a new player for the game with publicID=gameID
with the given parameters.
Payload
{ "publicID": [string], // 255 characters max, must be unique for a given game "name": [string], // 2000 characters max "metadata": [JSON] }
Metadata is intended to include all the player’s game specific informations that are not directly related to the khan’s clan management. For example, player ranking, player trophies, player level, etc.
Success Response
Code:
200
Content:
{ "success": true, "publicID": [string] // player public id }
Error Response
It will return an error if an invalid payload is sent or if there are missing parameters.
Code:
400
Content:
{ "success": false, "reason": [string] }
It will return an error if there are invalid parameters.
Code:
422
Content:
{ "success": false, "reason": [string] }
Code:
500
Content:
{ "success": false, "reason": [string] }
Update Player¶
PUT /games/:gameID/players/:playerPublicID
Updates the player with the given publicID.
Payload
{ "name": [string], // 2000 characters max "metadata": [JSON] }
Success Response
Code:
200
Content:
{ "success": true }
Error Response
It will return an error if an invalid payload is sent or if there are missing parameters.
Code:
400
Content:
{ "success": false, "reason": [string] }
It will return an error if there are invalid parameters.
Code:
422
Content:
{ "success": false, "reason": [string] }
Code:
500
Content:
{ "success": false, "reason": [string] }
Retrieve Player¶
GET /games/:gameID/players/:playerPublicID
Gets the player with the given publicID.
Success Response
Code:
200
Content:
{ "success": true, "publicID": [string], // Player publicID "name": [string], // Player Name "metadata": [JSON], // Player Metadata "createdAt": [int64], // timestamp in milliseconds of when the player was created "updatedAt": [int64] // timestamp in milliseconds of when the player was last updated //All clans the player is involved with show here "clans":{ // Clans the player ownership "owned":[ { "name": [string], "publicID": [string] }, // clan name and publicID ], // Clans the player has been approved and is currently a member of "approved":[ { "name": [string], "publicID": [string] }, // clan name and publicID ], // Clans the player has been banned from "banned":[ { "name": [string], "publicID": [string] }, // clan name and publicID ], // Clans the player has been rejected from "denied":[ { "name": [string], "publicID": [string] }, // clan name and publicID ], // Clans the player has pending applications to "pendingApplications":[ { "name": [string], "publicID": [string] }, // clan name and publicID ], // Clans the player has pending invites to "pendingInvites":[ { "name": [string], "publicID": [string] }, // clan name and publicID ] }, // All memberships this player has with details "memberships":[ { //if approved, denied and banned are false, the membership is pending approval "approved": [bool], "denied": [bool], "banned": [bool], //clan the player applied to "clan":{ "metadata": [JSON], "name": [string], "publicID": [string], "membershipCount": [int] }, "createdAt": [int64], // timestamp the player applied to a clan "updatedAt": [int64], // timestamp that the membership was last updated "deletedAt": [int64], // timestamp that the player was banned "approvedAt": [int64], // timestamp that the player was approved "deniedAt": [int64], // timestamp that the player was denied "level": [string], // level of the player in this clan "message": [string], // empty if membership not created using an application // or the application message otherwise // Player that requested membership // If the player was invited, this should be another player. // Otherwise, this is the same as the player. // If the membership level is 'owner' this key does not exist. "requestor":{ "publicID": [string] "name": [string], "metadata": [JSON], }, // Player that approved this membership // If the membership is not yet approved or the level is 'owner' this key does not exist "approver":{ "publicID": [string] "name": [string], "metadata": [JSON], }, // Player that denied this membership // If the membership is not yet denied "denier":{ "publicID": [string] "name": [string], "metadata": [JSON], }, } ] }
Error Response
Code:
500
Content:
{ "success": false, "reason": [string] }
Clan Routes¶
Create Clan¶
POST /games/:gameID/clans
Creates a new clan for the game with publicID=gameID
with the given parameters.
Payload
{ "publicID": [string], // 255 characters max, must be unique for a given game "name": [string], // 2000 characters max "metadata": [JSON], "ownerPublicID": [string], // must reference an existing player "allowApplication": [boolean], "autoJoin": [boolean] }
Metadata is intended to include all the clan’s game specific informations that are not directly related to the khan’s clan management.
Some parameters require special attention:
allowApplication: if set to false only the clan owner and members can invite players to join the clan, otherwise players can request to be added to a given clan.
autoJoin: if set to true, when a player applies their membership is automatically approved. If set to false, the clan owner or one of its members must approve or deny the player’s application.
Success Response
Code:
200
Content:
{ "success": true, "publicID": [string] // clan public id }
Error Response
It will return an error if an invalid payload is sent or if there are missing parameters.
Code:
400
Content:
{ "success": false, "reason": [string] }
Code:
500
Content:
{ "success": false, "reason": [string] }
Update Clan¶
PUT /games/:gameID/clans/:clanPublicID
Updates the clan with the given publicID.
Payload
{ "name": [string], // 2000 characters max "metadata": [JSON], "ownerPublicID": [string], // must match the clan owner's public id "allowApplication": [boolean], "autoJoin": [boolean] }
All parameters but the
ownerPublicID
will be updated.Success Response
Code:
200
Content:
{ "success": true }
Error Response
It will return an error if an invalid payload is sent or if there are missing parameters.
Code:
400
Content:
{ "success": false, "reason": [string] }
Code:
500
Content:
{ "success": false, "reason": [string] }
Retrieve Clan¶
GET /games/:gameID/clans/:clanPublicID
Optional query string argument: shortID, if true
then the first 8 characters of clanID are accepted.
Retrieves the clan with the given publicID. It will list all the clan information and its members.
The roster, as well as the memberships return a list of players, following this structure:
{
"level": [string], // not returned for denied/banned memberships
"message": [string], // the message sent with the application
// or "" if the membership was not created with and application
"player": {
"publicID": [string],
"name": [string],
"metadata": [JSON],
"approver": { // player that approved this membership
"publicID": [string],
"name": [string],
}
}
}
Success Response
Code:
200
Content:
{ "publicID": [string], "success": true, "name": [string], "metadata": [JSON], "allowApplication": [bool], "autoJoin": [bool], "membershipCount": [int], "owner": { "publicID": [string], "name": [string], "metadata": [JSON], }, "roster": [ [membership], //a list of the above membership structure ], "memberships": { "pendingApplications": [ [membership], //a list of all the pending applications in this clan ], "pendingInvites": [ [membership], //a list of all the pending invites in this clan ], "denied": [ [membership], //a list of all the denied memberships in this clan ], "banned": [ [membership], //a list of all the banned memberships in this clan ], } }
Error Response
Code:
500
Content:
{ "success": false, "reason": [string] }
Clan Summary¶
GET /games/:gameID/clans/:clanPublicID/summary
Returns a summary of the details of the clan with the given publicID.
Success Response
Code:
200
Content:
{ "success": true, "publicID": [string], "name": [string], "metadata": [JSON], "allowApplication": [bool], "autoJoin": [bool], "membershipCount": [int] }
Error Response
Code:
500
Content:
{ "success": false, "reason": [string] }
Clans Summary¶
GET /games/:gameID/clans-summary?clanPublicIds=clan1,clan2,clan3
Returns a summary of the details for each one of the clans with the given publicIDs.
Success Response
Code:
200
Content:
{ "success": true, clans: [ { "publicID": [string], "name": [string], "metadata": [JSON], "allowApplication": [bool], "autoJoin": [bool], "membershipCount": [int] }, { "publicID": [string], "name": [string], "metadata": [JSON], "allowApplication": [bool], "autoJoin": [bool], "membershipCount": [int] }, ... ] }
If no clanPublicIDs are provided:
Error Response
Code:
400
Content:
{ "success": false, "reason": [string] }
If at least one of the clan was not found:
Error Response
Code:
404
Content:
{ "success": false, "reason": [string] }
In case of other errors:
Error Response
Code:
500
Content:
{ "success": false, "reason": [string] }
List Clans¶
GET /games/:gameID/clans
List all clans for the game with publicID=gameID
.
Warning
Depending on the number of clans in your game this can be a VERY expensive operation! Be wary of using this. A better way of getting clans is using clan search.
Success Response
Code:
200
Content:
{ "success": true, "clans": [ { "name": [string], "metadata": [JSON], "membershipCount": [int], "publicID": [string], "allowApplication": [bool], "autoJoin": [bool] } ] }
An empty list will be returned if there are no clans for the given game.
Search Clans¶
GET /games/:gameID/clans/search
Searches for clans of a given game where the name include the term passed in the query string, or term is a publicID.
Results are limited by “search.pageSize” set via config YAML or environment variable KHAN_SEARCH_PAGESIZE
URL Parameters
term=[string]
Success Response
Code:
200
Content:
{ "success": true, "clans": [ { "name": [string], "metadata": [JSON], "membershipCount": [int], "publicID": [string], "allowApplication": [bool], "autoJoin": [bool] } ] }
An empty list will be returned if no clans match the term.
Error Response
It will return an error if an empty search term is sent.
Code:
400
Content:
{ "success": false, "reason": "A search term was not provided to find a clan." }
Leave Clan¶
POST /games/:gameID/clans/:clanPublicID/leave
Allows the owner to leave the clan. If there are no clan members the clan will be deleted. Otherwise, the new clan owner will be the member with the highest level which has the oldest creation date.
Success Response
Code:
200
Content:
{ "success": true, "isDeleted": [bool], //Indicates whether the clan was deleted //because there were no members left "previousOwner": { "publicID": [string], "name": [string], "metadata": [JSON], "membershipCount": [int], "ownershipCount": [int] }, "newOwner": { "publicID": [string], "name": [string], "metadata": [JSON], "membershipCount": [int], "ownershipCount": [int] } }
Error Response
It will return an error if clan is not found.
Code:
400
Content:
{ "success": false, "reason": [string] }
Code:
500
Content:
{ "success": false, "reason": [string] }
Transfer Clan Ownership¶
POST /games/:gameID/clans/:clanPublicID/transfer-ownership
Allows the owner to transfer the clan’s ownership to another clan member of their choice. The previous owner will then be a member with the maximum level allowed for the clan.
Payload
{ "playerPublicID": [string] // must match a clan member's public id }
Success Response
Code:
200
Content:
{ "success": true, "previousOwner": { "publicID": [string], "name": [string], "metadata": [JSON], "membershipCount": [int], "ownershipCount": [int] }, "newOwner": { "publicID": [string], "name": [string], "metadata": [JSON], "membershipCount": [int], "ownershipCount": [int] } }
Error Response
It will return an error if an invalid payload is sent or if there are missing parameters.
Code:
400
Content:
{ "success": false, "reason": [string] }
Code:
500
Content:
{ "success": false, "reason": [string] }
Membership Routes¶
Apply For Membership¶
POST /games/:gameID/clans/:clanPublicID/memberships/application
Allows a player to ask to join the clan with the given publicID. If the clan’s autoJoin property is true the member will be automatically approved. Otherwise, the membership must be approved by the clan owner or one of the clan members.
Payload
{ "level": [string], // the level of the membership "playerPublicID": [string], // the player's public id "message": [string] // optional, a message sent by the player }
Success Response
Code:
200
Content:
{ "success": true, "approved": [bool] // it will be true if the membership does not require additional approval (i.e. clan autoJoin is true) }
Error Response
It will return an error if an invalid payload is sent or if there are missing parameters.
Code:
400
Content:
{ "success": false, "reason": [string] }
Code:
500
Content:
{ "success": false, "reason": [string] }
Approve Or Deny Membership Application¶
POST /games/:gameID/clans/:clanPublicID/memberships/application/:action
:action
must be either ‘approve’ or ‘deny’.
Allows the clan owner or a clan member to approve or deny a player’s application to join the clan. The member’s membership level must be at least the game’s minLevelToAcceptApplication
.
Payload
{ "playerPublicID": [string] // the public id of player who made the application "requestorPublicID": [string] // the public id of the clan member or the owner who will approve or deny the application }
Success Response
Code:
200
Content:
{ "success": true }
Error Response
It will return an error if an invalid payload is sent or if there are missing parameters.
Code:
400
Content:
{ "success": false, "reason": [string] }
Code:
500
Content:
{ "success": false, "reason": [string] }
Invite For Membership¶
POST /games/:gameID/clans/:clanPublicID/memberships/invitation
Allows the clan owner or a clan member to invite a player to join the clan with the given publicID. If the request is made by a member of the clan, their membership level must be at least the game’s minLevelToCreateInvitation
. The membership must be approved by the player being invited.
Payload
{ "level": [string], // the level of the membership "playerPublicID": [string], // the public id player being invited "requestorPublicID": [string] // the public id of the member or the clan owner who is inviting }
Success Response
Code:
200
Content:
{ "success": true }
Error Response
It will return an error if an invalid payload is sent or if there are missing parameters.
Code:
400
Content:
{ "success": false, "reason": [string] }
Code:
500
Content:
{ "success": false, "reason": [string] }
Approve Or Deny Membership Invitation¶
POST /games/:gameID/clans/:clanPublicID/memberships/invitation/:action
:action
must be either ‘approve’ or ‘deny’.
Allows a player member to approve or deny a player’s invitation to join a given clan.
Payload
{ "playerPublicID": [string] // the public id of player who was invited }
Success Response
Code:
200
Content:
{ "success": true }
Error Response
It will return an error if an invalid payload is sent or if there are missing parameters.
Code:
400
Content:
{ "success": false, "reason": [string] }
Code:
500
Content:
{ "success": false, "reason": [string] }
Promote Or Demote Member¶
POST /games/:gameID/clans/:clanPublicID/memberships/:action
:action
must be either ‘promote’ or ‘demote’.
Allows the clan owner or a clan member to promote or demote another member. When promoting, the member’s membership level will be increased by one, when demoting it will be decreased by one. The member’s membership level must be at least minLevelOffsetToPromoteMember
or minLevelOffsetToDemoteMember
levels greater than the level of the player being promoted or demoted.
Payload
{ "playerPublicID": [string], // the public id player being promoted or demoted "requestorPublicID": [string] // the public id of the member or the clan owner who is promoting or demoting }
Success Response
Code:
200
Content:
{ "success": true }
Error Response
It will return an error if an invalid payload is sent or if there are missing parameters.
Code:
400
Content:
{ "success": false, "reason": [string] }
Code:
500
Content:
{ "success": false, "reason": [string] }
Delete Membership¶
POST /games/:gameID/clans/:clanPublicID/memberships/delete
Allows the clan owner or a clan member to remove another member from the clan. The member’s membership level must be at least minLevelToRemoveMember
. A member can leave the clan by sending the same playerPublicID
and requestorPublicID
.
Payload
{ "playerPublicID": [string], // the public id player being deleted "requestorPublicID": [string] // the public id of the member or the clan owner who is deleting the membership }
Success Response
Code:
200
Content:
{ "success": true }
Error Response
It will return an error if an invalid payload is sent or if there are missing parameters.
Code:
400
Content:
{ "success": false, "reason": [string] }
Code:
500
Content:
{ "success": false, "reason": [string] }
Pruning Stale Data¶
Depending on your usage of Khan, you will accumulate some stale data. Some examples include:
- Pending Applications to a clan;
- Pending Invitations to a clan;
- Deleted Memberships (member left or was banned);
- Denied Memberships.
While there’s nothing wrong with keeping this data in the Data Store, it will slow Khan down considerably depending on your usage of it.
Gotchas¶
One good example of how this sort of stale data can go wrong is when building a clan suggestion for players. If you always suggest the same clans, eventually those clans will have thousands of unfulfilled applications.
That way, anytime someone requests info for these clans, Khan will have a hard time to fulfill that request.
Pruning Stale Data¶
Khan has a prune
command built-in, designed for the purpose of keeping your data balanced. It looks for some configuration keys in your games, and then decides on what data should be deleted.
WARNING¶
This command performs a HARD delete on the memberships row and can’t be undone. Please ensure you have frequent backups of your data store before applying pruning.
Configuring Games to be Pruned¶
Configuring a game to be pruned is as easy as including some keys in the game’s metadata property:
pendingApplicationsExpiration
: the number of SECONDS to wait before deleting a pending application;pendingInvitesExpiration
: the number of SECONDS to wait before deleting a pending invitation;deniedMembershipsExpiration
: the number of SECONDS to wait before deleting a denied membership;deletedMembershipsExpiration
: the number of SECONDS to wait before deleting a deleted membership (either the member left or was banned).
PLEASE take note that all the expirations are in SECONDS. The timestamp used to compare the expiration to is the updated_at
field of memberships.
Khan will delete any membership that meets one of the criteria above AND has an updated_at
timestamp older than the relevant configuration subtracted in seconds from NOW.
NOTICE¶
If you want a game to be pruned, ALL expiration keys MUST be set. Otherwise, Khan will ignore that game as far as pruning goes.
Periodically Running Pruning¶
Khan’s command line for pruning is:
$ khan prune -c /path/to/config.yaml
Khan will use the connection details in your specified config file. Double-check the config file being used to ensure that you won’t lose any unwanted information.
Pruning with a Container¶
Since Khan has container offers, you can also use a container for running pruning in any PaaS that supports Docker containers.
In order to use it, you need to configure these environment variables in the container:
KHAN_POSTGRES_HOST
- PostgreSQL to prune hostname;KHAN_POSTGRES_PORT
- PostgreSQL to prune port;KHAN_POSTGRES_USER
- PostgreSQL to prune username;KHAN_POSTGRES_PASSWORD
- PostgreSQL to prune password;KHAN_POSTGRES_DBNAME
- PostgreSQL to prune database name;KHAN_SENTRY_URL
- Sentry URL to send errors to. If you do not use sentry, just leave this unset;KHAN_PRUNING_SLEEP
- Number of seconds to sleep between pruning operations. Defaults to 3600.
The image can be found at our official Docker Hub repository.
Using Postman with Khan¶
Khan supports Postman to make it easier on users to test their Khan server.
Using Postman with Khan is as simple as importing it’s operations and environment into Postman.
Importing Khan’s environment¶
To import Khan’s environment, download it’s environment file and import it in Postman.
Importing Khan’s operations¶
To import Khan’s operations, download it’s operations file and import it in Postman.
Running Khan’s operations with a different environment¶
Just configure a new environment and make sure it contains the baseKhanURL
variable with a value like http://my-khan-server/
. Do not forget the ending slash or it won’t work.
Khan’s Benchmarks¶
You can see khan’s benchmarks in our CI server as they get run with every build.
Creating the performance database¶
To create a new database for running your benchmarks, just run:
$ make drop-perf migrate-perf
Running Benchmarks¶
If you want to run your own benchmarks, just download the project, and run:
$ make run-test-khan run-perf
Generating test data¶
If you want to run your perf tests against a database with more volume of data, just run this command prior to running the above one:
$ make drop-perf migrate-perf db-perf
Warning: This will take a long time running (around 30m).
Results¶
The results should be similar to these:
BenchmarkCreateClan-2 2000 3053999 ns/op
BenchmarkUpdateClan-2 2000 2000650 ns/op
BenchmarkRetrieveClan-2 500 10522248 ns/op
BenchmarkRetrieveClanSummary-2 5000 1187486 ns/op
BenchmarkSearchClan-2 5000 1205325 ns/op
BenchmarkListClans-2 5000 1135555 ns/op
BenchmarkLeaveClan-2 1000 3824284 ns/op
BenchmarkTransferOwnership-2 500 8642818 ns/op
BenchmarkCreateGame-2 3000 1248042 ns/op
BenchmarkUpdateGame-2 2000 2141705 ns/op
BenchmarkApplyForMembership-2 1000 5695344 ns/op
BenchmarkInviteForMembership-2 500 8916792 ns/op
BenchmarkApproveMembershipApplication-2 500 13480574 ns/op
BenchmarkApproveMembershipInvitation-2 1000 10517905 ns/op
BenchmarkDeleteMembership-2 500 9548314 ns/op
BenchmarkPromoteMembership-2 500 8961424 ns/op
BenchmarkDemoteMembership-2 500 9202060 ns/op
BenchmarkCreatePlayer-2 3000 1344267 ns/op
BenchmarkUpdatePlayer-2 3000 1829329 ns/op
BenchmarkRetrievePlayer-2 300 14412830 ns/op