Specification for V1 - snowplow-archive/redash-java-sdk GitHub Wiki

Introduction

We want to utilise Redash in a different way than the usual user in that we will be adding hundreds of datasources, users and groups to the service to ensure a strict and accurate ACL for all people using said service.

All of the actions we need to take are currently being done manually by a person in a user interface. We need to move these interactions to code and then to fully automate them.

Interactions

The following interactions are required:

  1. Create new data-source
  2. Update specific data-source
  3. List all data-sources
  4. Delete specific data-source
  5. Create new user-group
  6. Add user to specific user-group(s)
  7. Add data-source to specific user-group(s)
  8. Remove user from specific user-group(s)
  9. Remove data-source from specific user-groups(s)
  10. List all user-groups
  11. Delete specific user-group
  12. List all users
  13. Get specific user
  14. Get specific data-source

Note: Users are expected to be available ahead-of-time.

1. Create new data-source

Initial support is only required for Redshift - however the design of this function should support easy extension of other target types.

Command in cURL:

curl -XPOST https://some-redash-server/api/data_sources?api_key=xxx \
	-d '{"options":{"host":"host","port":5439,"user":"user","password":"password","dbname":"database"},"type":"redshift","name":"name"}' \
	-H "Accept: application/json, text/plain, */*" \
	-H "Content-Type: application/json;charset=UTF-8"

Example in Java:

RedshiftDataSource rds = new RedshiftDataSource.RedshiftDataSourceBuilder("name")
	.host("host")
	.port(5439)
	.user("user")
	.password("password")
	.dbname("database")
	.build();

int result = redashClient.createDataSource(rds);

// If data-source could not be created throw IOException
// If data-source with this name already exists throw IllegalArgumentException
// If data-source created successfully return id of created source

2. Update specific data-source

Allows a source to have usernames / passwords updated or changed. Important for our ability to rotate credentials regularly.

Example in Java:

// NOTE: All data-sources should inherit from a base DataSource type

RedshiftDataSource rds = new RedshiftDataSource.RedshiftDataSourceBuilder("name")
	.host("updatedHost")
	.port(5439)
	.user("user")
	.password("updatedPassword")
	.dbname("database")
	.build();

boolean result = redashClient.updateDataSource(rds);

// If data-source could not be updated throw IOException
// If data-source is already up-to-date return "false"
// If data-source updated successfully return "true"

3. List all data-sources

Command in cURL:

curl -XGET https://some-redash-server/api/data_sources?api_key=xxx

Example in Java:

List<DataSource> dataSourceList = new ArrayList<>();
dataSourceList = redashClient.getDataSources();

// If data-sources could not be fetched throw IOException
// If data-sources could be queried return List<DataSource> - zero results is not a failure

4. Delete specific data-source

Command in cURL:

curl -XDELETE https://some-redash-server/api/data_sources/{{ data_source_id }}?api_key=xxx

Example in Java:

boolean result = redashClient.deleteDataSource(id);

// If data-source could not be deleted throw IOException
// If data-source with provided id could not be found return "false"
// If data-source was found and deleted return "true"

5. Create new user-group

Command in cURL:

curl -XPOST https://some-redash-server/api/groups?api_key=xxx \
	-d '{"name": "yyy"}' \
	-H "Accept: application/json, text/plain, */*" \
	-H "Content-Type: application/json;charset=UTF-8"

Example in Java:

UserGroup userGroup = new UserGroup("some-name");
int result = redashClient.createUserGroup(userGroup);

// If user-group could not be created throw IOException
// If user-group with this name already exists throw IllegalArgumentException
// If user-group created successfully return id of created group

6. Add user to specific user-group(s)

Command in cURL:

curl -XPOST https://some-redash-server/api/groups/{{ group_id }}/members?api_key=xxx \
	-d '{"user_id": "{{ user_id }}"}' \
	-H "Accept: application/json, text/plain, */*" \
	-H "Content-Type: application/json;charset=UTF-8"

Example in Java:

boolean result = redashClient.addUserToGroup(userId, userGroupId);

// If user could not be added to group throw IOException
// If user does not exist throw IllegalArgumentException
// If user-group does not exist throw IllegalArgumentException
// If user already member of group return "false"
// If user added successfully to group return "true"

7. Add data-source to specific user-group(s)

Command in cURL:

curl -XPOST https://some-redash-server/api/groups/{{ group_id }}/data_sources?api_key=xxx \
	-d '{"data_source_id": "{{ data_source_id }}"}' \
	-H "Accept: application/json, text/plain, */*" \
	-H "Content-Type: application/json;charset=UTF-8"

Example in Java:

boolean result = redashClient.addDataSourceToGroup(dataSourceId, userGroupId);

// If data-source could not be added to group throw IOException
// If data-source does not exist throw IllegalArgumentException
// If user-group does not exist throw IllegalArgumentException
// If data-source already attached to group return "false"
// If data-source added successfully to group return "true"

8. Remove user from specific user-group(s)

Command in cURL:

curl -XDELETE https://some-redash-server/api/groups/{{ group_id }}/members/{{ user_id }}?api_key=xxx

Example in Java:

boolean result = redashClient.removeUserFromGroup(userId, userGroupId);

// If user could not be removed from group throw IOException
// If user does not exist throw IllegalArgumentException
// If user-group does not exist throw IllegalArgumentException
// If user not a member of group return "false"
// If user removed successfully from group return "true"

9. Remove data-source from specific user-groups(s)

Command in cURL:

curl -XDELETE https://some-redash-server/api/groups/{{ group_id }}/data_sources/{{ data_source_id }}?api_key=xxx

Example in Java:

boolean result = redashClient.removeDataSourceFromGroup(dataSourceId, userGroupId);

// If data-source could not be removed from group throw IOException
// If data-source does not exist throw IllegalArgumentException
// If user-group does not exist throw IllegalArgumentException
// If data-source not attached to group return "false"
// If data-source removed successfully from group return "true"

10. List all user-groups

Command in cURL:

curl -XGET https://some-redash-server/api/groups?api_key=xxx

Example in Java:

List<UserGroup> userGroupList = new ArrayList<>();
userGroupList = redashClient.getUserGroups();

// Each user-group should include both the name and id

// If user-groups could not be fetched throw IOException
// If user-groups could be found - return them

11. Delete specific user-group

Command in cURL:

curl -XDELETE https://some-redash-server/api/groups/{{ group_id }}?api_key=xxx

Example in Java:

boolean result = redashClient.deleteUserGroup(userGroupId)

// If user-group could not be deleted throw IOException
// If user-group with provided id could not be found return "false"
// If user-group was found and deleted return "true"

12. List all users

Command in cURL:

curl -XGET https://some-redash-server/api/users?api_key=xxx

Example in Java:

List<User> userList = new ArrayList<>();
userList = redashClient.getUsers();

// Each user should include both the name & id

// If we cannot query for users throw IOException
// If users found (or not) return them as a list of users

13. Get user

Command in cURL:

curl -XGET https://some-redash-server/api/users?api_key=xxx

Note: There is no API command to fetch a particular user - so all users will need to be fetched and then searched for the specified username.

Example in Java:

User user = redashClient.getUser("username");

// If we cannot query for users throw IOException
// If user does not exist throw IllegalArgumentException
// If user found return the User object with name & id

14. Get data-source

Command in cURL:

curl -XGET https://some-redash-server/api/data_sources?api_key=xxx

Note: There is no API command to fetch a particular data-source - so all data-sources will need to be fetched and then searched for the specified source.

Example in Java:

DataSource dataSource = redashClient.getDataSource("sourceName");

// If we cannot query for data-sources throw IOException
// If data-source does not exist throw IllegalArgumentException
// If data-source found return the DataSource

Requirements

Technology

The library must:

  1. Be implemented as a Java 8 based library
  2. Authenticate with the Redash server via a supplied host, port and API key in a Java builder pattern
  3. Be built using the Gradle build system
  4. Use OkHTTP for all required HTTP communications

Testing

The SDK must include a full suite of tests to ensure each above endpoint behaves as expected. This will require a set of tests implementing Mockito to ensure that code paths behave correctly as well as several integration tests with user-scenarios that we will need to mimic.

The user testing scenarios will require a redash instance to be configured and setup locally on the CI servers.

UA - 1

I need to add a new user-group to redash. Once added I need to add a specific already created user to this group and then attach a data-source to this group.

After sometime has passed I need to remove this data-source from this group.

// Create new Group
UserGroup userGroup = new UserGroup("test group");
int userGroupId = redashClient.createUserGroup(userGroup);

// Get user
User user = redashClient.getUser("username");

// Add user to group
boolean addUserToGroupResult = redashClient.addUserToGroup(user.getId(), userGroupId);

// Get data-source
DataSource dataSource = redashClient.getDataSource("source"); 

// Add data-source to group
boolean addDataSourceToGroupResult = redashClient.addDataSourceToGroup(dataSource.getId(), userGroupId);

// Remove data-source from group
boolean removeDataSourceFromGroupResult = redashClient.removeDataSourceFromGroup(dataSource.getId(), userGroupId);

UA - 2

I need to add a new Redshift data-source to redash. Once added I need to attach this data-source to all created groups.

// Create new data-source
RedshiftDataSource rds = new RedshiftDataSource.RedshiftDataSourceBuilder("name")
	.host("host")
	.port(5439)
	.user("user")
	.password("password")
	.dbname("database")
	.build();
int result = redashClient.createDataSource(rds);

// Get all user groups
List<UserGroup> userGroupList = new ArrayList<>();
userGroupList = redashClient.getUserGroups();

// Attach the data-source to all groups
for (UserGroup ug : userGroupList) {
  boolean addDataSourceToGroupResult = redashClient.addDataSourceToGroup(result, ug.getId());
}

Packaging & deployment (TO BE HANDLED BY SNOWPLOW)

The library must be deployed to our Snowplow Bintray Maven and must be easily included in other gradle projects.

The deployment will be triggered via tag creation on the repo which will trigger a TravisCI build. On Travis:

  1. All tests will need to be run
  2. The library will need to be built
  3. The library will need to be published to Bintray

License

This is an open source project for Snowplow. As such a license will need to be included within the root of the project and at the top of every source file stating this.

Within the root of the project a file named LICENSE-2.0.txt should contain the same contents as this link:

https://github.com/snowplow/snowplow/blob/master/LICENSE-2.0.txt

For the headers within the source files of the project:

/*
 * Copyright (c) 2018 Snowplow Analytics Ltd. All rights reserved.
 *
 * This program is licensed to you under the Apache License Version 2.0,
 * and you may not use this file except in compliance with the Apache License Version 2.0.
 * You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the Apache License Version 2.0 is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
 */
⚠️ **GitHub.com Fallback** ⚠️