IAM (Identity and Access Management) is used for maintaining and checking permissions between resources.
RI Email (Rotary International) is used for verify email uniqueness in the whole RI system.
DIS 3.0 provides all services via RESTful API endpoints.All APIs follow OpenAPI 3.0 standard where full specifications and schemas can be found in our APIS.
MemberClub
MemberClub is the main service that handles club and individual related logic.
Search Service
SearchService provides search APIs to resources, like individuals and clubs.
IAM
IAM (Identity and Access Management) is used for maintaining and checking permissions between resources.
RI Email
RI Email (Rotary International) is used for verify email uniqueness in the whole RI system.
Ocp-Apim-Subscription-KeyOcp-Apim-Subscription-Key
is the identity of your application. DIS will determine your access level based on this key, It must be provided in header during every API call.
IdentityIdentity
header is usually required as an ID field for specifying who is executing this request. DIS will determine if the action can proceed based on the access level of this requestor to the resource that is being read/written."
ImpersonatorIdImpersonatorId
is the staffId who wants to access a user's account.
HATEOAS
HATEOAS is an RESTful architectural style which provides hypermedia links in the response contents so that the client can dynamically navigate to the appropriate resource by traversing the hypermedia links. Please refer to REST API Tutorial.
JSON Patch
JSON Patch is a format for describing changes to a JSON document. It can be used to avoid sending a whole document when only a part has changed. When used in combination with the HTTP PATCH method, it allows partial updates for HTTP APIs in a standards compliant way.Please refer to jsonpatch.com.
Internal Client Access
Consumer with Internal subscription key.
External Client Access
Consumer with External subscription key.
DIS Client Access
Consumer with DIS subscription key. According to the minimum permission principle, there's no permission of DIS key by default. Apply higher permission for DIS key when a feature need it.
Impersonation allows a staff acts as a user and operate the user's data. ImpersonatorId needs to be provided in the header and API will validate the authorization, the staff can only impersonate the user after being authorized.
The authorization process is to validate Okta session, if the session is valid, the authorization will be successful.
If the session is invalid or session expired, the authorization will fail and API will return 401 error.
DIS 3.0 would have 3 type of services.
Internal Service and External Service are the consumers of DIS 3.0. We distinguish them based on whether the application needs the end-user credential. In general, an Internal Service is a highly-trust application and no end-user authorization is needed. An example is a cron job that uses an API to export information for reporting. An External Service is an application which needs the end-user credentials. Sometimes an application would be Internal Service and External Service at the same time, e.g. MyRotary is an application includes a backend and frontend(s), it is a highly-trust application, which has a cron job to fetch all individuals information for reporting, now it's acting like an Internal Service. It also has some feature consume DIS 3.0 on behalf of end-user, now it's acting as an External Service. Internal Service could access any data that a consumer could access. External Service could access data based on end-user permission.
DIS Service(MemberClub, IAM, SearchService, RIEmail), which is the implementation of DIS 3.0 itself, could potentially access any needed data in DIS 3.0 scope. However, based on the principle of least privilege, it's better to add it to the white list of specific API when needed. For example, there are some APIs in IAM, like ResourceOwner and Affiliation, could not be accessed by Internal Service and External Service but DIS Service.
Please contact your point of contact to acquire your Ocp-Apim-Subscription-Key
key before continuing to the next steps.
curl -X POST \
https://ri-d-usc-dis-am.azure-api.net/memberclub/individuals \
-H 'Content-Type: application/json' \
-H 'Ocp-Apim-Subscription-Key: [YOUR_KEY_HERE]' \
-d '{
"firstName": "Lucy",
"lastName": "Jones",
"gender": "Female",
"primaryLanguage": {
"languageId": "abk"
}
}'
Response with HATEOAS links:
If the requestor has no permission to the target resource from IAM, DIS3 will return a link with empty method.
HTTP/1.1 201 Created
Content-Type: application/hal+json; charset=utf-8
{
"id": "[INDIVIDUAL_ID]",
"firstName": "Lucy",
"lastName": "Jones",
"gender": "Female",
"primaryLanguage": {
"language": "Abkhazian",
"languageId": "abk"
},
"_links": {
"self": {
"href": "/individuals/[INDIVIDUAL_ID]",
"method": "GET|PATCH"
},
"photo": {
"href": "/individuals/[INDIVIDUAL_ID]/photo",
"method": "PUT|DELETE"
},
"onlineid": {
"href": "/individuals/[INDIVIDUAL_ID]/onlineid",
"method": ""
},
"programs": {
"href": "/individuals/[INDIVIDUAL_ID]/programs",
"method": "GET|POST"
}
}
}
curl -X PATCH \
https://ri-d-usc-dis-am.azure-api.net/memberclub/individuals/[TARGET_INDIVIDUAL_ID_HERE] \
-H 'Content-Type: application/json' \
-H 'Identity: [REQUESTOR_INDIVIDUAL_ID_HERE]' \
-H 'ImpersonatorId: [STAFF_ID_FOR_IMPERSONATON]' \
-H 'Ocp-Apim-Subscription-Key: [YOUR_KEY_HERE]' \
-d '[
{
"op": "replace",
"path": "/primaryAddress",
"value": {
"type": "Home",
"lineOne": "primary-update",
"lineTwo": "LineTwo",
"lineThree": "LineThree",
"countryId": "USA",
"city": "NewYork",
"stateId": "AA",
"internationalProvince": "",
"postalCode": "100001"
}
}
]'
Response:
HTTP/1.1 200 OK
GET /metadata/countries
GET /metadata/states
GET /metadata/languages
GET /metadata/expertise-areas
GET /metadata/expertise-levels
GET /metadata/leadership-roles
GET /metadata/programs
GET /metadata/sharing-permission
GET /metadata/termination-reasons
GET /metadata/area-of-focus
{
"status": 400,
"type": "https://api.rotary.org/validation-error",
"title": "ValidationError",
"detail": "See errors for detailed information",
"errors": [
{
"code": "RequiredField",
"description": "individualId is required",
"source": "individualId",
"arguments": {}
}
]
}
{
"status": 404,
"type": "https://api.rotary.org/not-found",
"title": "NotFound",
"detail": "club is not found",
"source": "clubId"
}
Details for error code defination refer to DIS 3.0 Error Codes
400 Validation error or illegal operation
401 Authentication error
403 Forbidden
404 Resource not found
406 Header 'Accept' error
409 Conflict Exception
500 Bad data Exception or internalServer error, or Timeout in API Management
503 Temporary service error
504 Gateway Timeout, or request processing Timeout
The response sample will include all possible HTTP error code that listed here except 401 Unauthorized
and 403 Forbidden
. These two errors are common for all HAL APIs.
Example for 401 Unauthorized
{
"status": 401,
"type": "https://api.rotary.org/unauthorized",
"title": "Unauthorized",
"detail": "Impersonation session not found",
"source": "requestor, impersonatorId"
}
Example for 403 Forbidden
{
"status": 403,
"type": "https://api.rotary.org/forbidden",
"title": "Forbidden",
"detail": "No permission"
}
Example for 500 Timeout, which is return by API Management. The default Timeout of API Management is 240s.
Headers:
errorSource: forward-request
errorReason: Timeout
errorMessage: Request to the backend service timed out
errorSection: backend
Body:
{
"statusCode": 500,
"message": "Request to the backend service timed out"
}
Example for 504 Timeout, which is return by IAM Service. This error is raised when IAM waiting CosmosDB response over 60s.
Headers:
errorSource: forward-request
errorReason: Timeout
errorMessage: Request to the backend service timed out
errorSection: backend
Body:
{
"Status": 504,
"Type": "https://api.rotary.org/gateway-timeout",
"Title": "LockTimeout",
"Detail": "Timeout when processing request, please retry later"
}
One is who has the highest access level (View and edit) to the resource (individual, club, membership, leadership included)
For example:
Lucy is the manager to herself; she can view and edit her information.
If Lucy is an officer of one rotary club, she has the manager access level to the club profile and membership/leadership of this club. So, she can view and edit them.
One is who has the highest access level (View and edit) to the resource (individual profile) but has limitation to some resource.
For example:
If Lucy is an officer of one rotary club, she has the contributor access level to others’ profile. It means she can view and edit Lily’s information except photo (Lucy can view Lily’s photo only)
One only can view the resource (individual, club, membership, leadership included).
For example:
If Lily is a Rotarian (not an officer), she only can view the club’ s information.
One only can view the resource’s partly information.
For example:
If Lily's membership was terminated, she can view the club’ s information except some important information like phone number, mailing address etc.
One cannot view the resource
For example:
If Lily's membership was terminated, she cannot view any membership.
Some APIs have request parameters marked (optional) after the parameter name, but has a description like 'Required, NetForum riIndividualId, riIndividualId > 0', for example the api named 'GetIndividualByRiIndividualId'.
We use Azure API Management to apply authentication, authorization, and usage limits. As we mentioned before the API 'GetIndividualByRiIndividualId', the request URL should be 'https://ri-d-usc-dis-am.azure-api.net/memberclub/individuals[?riIndividualId]'. If we marked the riIndividualId without optional the API Management will check the request URL with parameter riIndividualId provided, and if you request the URL without the parameter the API Management will automatic response like:
{
"statusCode": 404,
"message": "Resource not found"
}
The request doesn't reach the backend service.
So we mark the parameter with optional and add the description to explain whether the parameter is required. If you request the URL without the required parameter, it will not be intercepted by the API Management and will be handled by the backend service. Then you will get the error message will more details like:
{
"status": 400,
"type": "https://api.rotary.org/validation-error",
"title": "ValidationError",
"detail": "See errors for detailed information",
"errors": [
{
"code": "Field_Required",
"description": "riIndividualId is required",
"source": "riIndividualId",
"arguments": {}
}
]
}
We want to make the error more detail. And it will tell you that the request lack parameters other than the URL not existed.
You can check the request body presenting as example and schema, you can switch between them in the detail of the API. The fields that are required in the request body will be marked in the schema.
{
"description": "",
"required": [
"individualId",
"type",
"admissionDate"
],
"type": "object",
"properties": {
"individualId": {
"type": "string"
},
"sponsorIds": {
"description": "Can only add sponsor to member in rotary club\nCan't add more than two sponsors\nSponsor can't be duplicated in the request\nSponsorId must be active member not honorary member",
"uniqueItems": false,
"type": "array",
"items": {
"format": "uuid",
"type": "string"
}
},
"type": {
"description": "type must be one of Member, Honorary",
"type": "string"
},
"admissionDate": {
"description": "admissionDate must be a valid date with format yyyy-MM-dd\nadmissionDate cannot be more than 1 day in the future (current date + 1)\nadmissionDate must be within 30 days",
"type": "string"
}
},
"example": {
"individualId": "a5087007-ec67-4085-844e-7c9587ecca9e",
"sponsorIds": [],
"type": "Member",
"admissionDate": "2019-12-03"
}
}