Given a file of the following structure:
-
slack: U123456
jira: test@example.org
-
slack: U234567
jira: other@example.org
email:
- other@example.org
- new@example.orgThis service answers questions of the following form:
For one that calls themselves
test@example.orgonjira, what is their username on Slack? (Answer:U123456).
A field may hold more than one name for the same person — write it as a
list, as email above. Then:
- A lookup matches any of the listed names. Asking about the one who is
other@example.orgornew@example.orgonemailfinds the same identity. - Asking for a multi-name field returns the whole list. The Slack user
U234567'semailresolves to["other@example.org", "new@example.org"].
A set of usernames related to a person is called an identity.
A field maps a service to one name, or to a list of names when a person uses several on that service.
Run:
cp .env.example .env
docker run --rm --interactive --tty -v $(pwd):/app composer:2.1 install
Note:
composer:2.1pins the composer version according to this repo'scomposer.lockpackages.
To install vendor dependencies. Then, to boot the containers, use:
./vendor/bin/sail up
While keeping the containers up (booted by sail up command), in the separate terminal window run:
./vendor/bin/sail artisan migrate
./vendor/bin/sail artisan db:seed
Note down the token given to you.
For convenience, you can setup a Bash Alias for the Sail command.
curl 'http://localhost/api/whose-name/query?u=test@example.org&s=jira&q=slack' \
-H "Accept: application/json" \
-H "Authorization: Bearer <YOURTOKEN>"
{"username":"U123456"}
Note: the Accept header is important for all requests.
The username field is a string when the asked service holds one name, an
array of strings when it holds several, and null (with a 404) when there
is no match:
curl 'http://localhost/api/whose-name/query?u=U234567&s=slack&q=email' \
-H "Accept: application/json" \
-H "Authorization: Bearer <YOURTOKEN>"
{"username":["other@example.org","new@example.org"]}
See the whose-name-client repository for a client of this API.
To resolve many identities in a single request, POST a list of queries to the
/api/whose-name/query/batch endpoint. Each query is a {"u","s","q"} triple with
the same meaning as the single query endpoint (u = known username, s = its service,
q = the service you ask about).
curl -X POST 'http://localhost/api/whose-name/query/batch' \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-H "Authorization: Bearer <YOURTOKEN>" \
-d '{"queries":[
{"u":"test@example.org","s":"jira","q":"slack"},
{"u":"other@example.org","s":"jira","q":"slack"},
{"u":"unknown","s":"unknown","q":"unknown"}
]}'
[{"u":"test@example.org","s":"jira","q":"slack","a":"U123456"},
{"u":"other@example.org","s":"jira","q":"slack","a":"U234567"},
{"u":"unknown","s":"unknown","q":"unknown","a":null}]
The response is an array of results in the same order as the queries. Each
result echoes its query (u, s, q) and carries the answer in a — a
string, an array of strings (when the asked service holds several names), or null
when no match was found. The endpoint returns:
200 OKwhen every query resolved to a username,207 Multi-Statuswhen at least one query returnednull,422 Unprocessable Entitywhen the request body is malformed (each query must provide non-emptyu,sandq; a batch may contain between 1 and 100 queries).
A pool is a named list of names sharing a single field (service). Pools live in
their own file (pools.yml by default):
-
name: Everyone
field: email
names:
- michal@makimo.pl
- alice@makimo.plBoth pool endpoints take p (the pool name) and q (the service you ask about).
Per-member — GET /api/whose-name/pool resolves every member on the asked
service and returns one result per member, in pool order, in the same
{u, s, q, a} shape and with the same status semantics as the batch query. For a
pool, u is the member's name, s the pool's field, q the asked service, and
a the answer (a string, an array of names, or null):
curl 'http://localhost/api/whose-name/pool?p=Everyone&q=jira' \
-H "Accept: application/json" \
-H "Authorization: Bearer <YOURTOKEN>"
[{"u":"michal@makimo.pl","s":"email","q":"jira","a":"jira1"},
{"u":"alice@makimo.pl","s":"email","q":"jira","a":["jira2","jira3"]}]
200 OKwhen every member resolved,207 Multi-Statuswhen at least one member resolved tonull,404 Not Found(with[]) when the pool is unknown or has no members.
When q is the pool's own field, the pool's names are returned verbatim.
Flattened — GET /api/whose-name/pool/names returns the same answers flattened
into a single, distinct list of names (arrays spread in, nulls and duplicates
dropped):
curl 'http://localhost/api/whose-name/pool/names?p=Everyone&q=jira' \
-H "Accept: application/json" \
-H "Authorization: Bearer <YOURTOKEN>"
{"names":["jira1","jira2","jira3"]}
Returns 200 OK, or 404 Not Found (with {"names":[]}) when nothing was found.
By default, the project uses the tests/whosename.yml file. The file contains two users and is not suited for more extensive work or running a working copy of the API.
If you'd like to change the path, modify the following entry in your .env file.
WHOSENAME_YAML=tests/whosename.yml
Pools are read from a separate file, configured the same way (defaults to
tests/pools.yml):
WHOSENAME_POOLS_YAML=tests/pools.yml
Because of Docker containers, the file must be located inside the repository.
For your convenience, the db:seed command created a user for you with the following credentials:
email: test@makimo.pl
password: test
If you need to create another user, use the user:create Artisan command:
./vendor/bin/sail artisan user:create someuser someone@example.org
The command will ask for a password for this user.
To request a token for a given user, you can use the /api/request-token API endpoint:
curl -X POST 'http://localhost/api/request-token' \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"email":"test@makimo.pl","password":"test","title":"test@my-laptop","abilities":["whose-name"]}'
Where:
emailandpasswordare valid user credentialstitleis a required token title. It should describe, where the token will be used (e.g."test@my-laptop")abilitiesis an array of abilities the token can access. For now, only thewhose-nameability is available.
This repository follows the following conventions:
- Tests communicate purpose of the code
- Domain separation into domain logic, infrastructure and application code
- Framework is a client of the domain, not the owner
- Architecture Decision Ledger to communicate design decisions
To get to know what does the code do, run the test suite:
./vendor/bin/sail test --group usage,behavior
To see all tests, including edge case tests, run the complete test suite:
./vendor/bin/sail test
See:
- tests for more information on how to use the codebase
- domain/WhoseName for domain objects. Specifically look for the Identity class.
- infrastructure/WhoseName for a Yaml file persistence implementation
- routes/api.php for endpoint definitions
- docs/adl for design decisions
Laravel is a web application framework with expressive, elegant syntax, written in PHP.
Laravel has a thorough documentation and video tutorial library, making it a breeze to get started with the framework.
A deploy workflow has been set up on the main branch for continuous deployment.
See .github/workflows/deploy.yml for more information.
The service is available under the whosename.makimo.pl address.
To manage users on production, you need access to access sites@lambdadelta.pl via SSH.
To add new users use the ./artisan user:create command. See Creating users and requesting tokens section for more information.
To change or remove users, use ./artisan tinker and play with App\Models\User Eloquent Model, e.g.
$user = App\Models\User::where('email', 'someone@example.org')->find();
$user->delete();The whose-name-data handles all data for this service. See it's README for more information.