Skip to content

Commit

Permalink
Merge branch 'release/0.17.0' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
ljupcovangelski committed Apr 13, 2021
2 parents 7009a4d + 21fe185 commit 440d184
Show file tree
Hide file tree
Showing 417 changed files with 6,821 additions and 1,417 deletions.
8 changes: 2 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
[![License](https://img.shields.io/github/license/airyhq/airy)](https://github.com/airyhq/airy/blob/develop/LICENSE)
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://github.com/airyhq/airy/projects)


---

![Airy_Explainer_Highlevel_Readme](https://user-images.githubusercontent.com/124274/113720584-18a8d500-96ef-11eb-97c3-362eebd6253d.jpeg)
Expand Down Expand Up @@ -43,6 +42,7 @@ amount of conversations and messages simultaneously and stream the relevant
conversational data to wherever you need it.

---

## About Airy

- **What does Airy do? 🚀**
Expand All @@ -64,6 +64,7 @@ conversational data to wherever you need it.
[The Airy Community will help](https://airy.co/community)

---

## Components

![Airy_Explainer_Components_Readme (1)](https://user-images.githubusercontent.com/12533283/112460661-6de3fe80-8d5f-11eb-8274-8446fbfcf5c8.png)
Expand All @@ -80,32 +81,27 @@ Kafka](https://kafka.apache.org) to process incoming webhook data from different
sources. We make sense of the data and reshape it into source independent
contacts, conversations, and messages.


-[APIs](https://airy.co/docs/core/api/introduction) to access your data

An [API](https://airy.co/docs/core/api/introduction) to access conversational
data with blazing fast HTTP endpoints.


- 🔌[WebSockets](https://airy.co/docs/core/api/websocket) to power real-time applications

A [WebSocket server](https://airy.co/docs/core/api/websocket) that allows
clients to receive near real-time updates about data flowing through the system.


- 🎣[Webhook](https://airy.co/docs/core/api/webhook) to listen to events and participate programmatically in conversations

A webhook integration server that allows its users to programmatically
participate in conversations by sending messages (the webhook integration
exposes events users can "listen" to and react programmatically.)


- 💎[UI: From an inbox to dashboards](https://airy.co/docs/core/apps/ui/introduction)

Not every message can be handled by code, this is why Airy comes with different
UIs ready for you and your teams to use.


## How to contribute

We welcome (and love) every form of contribution! Good entry points to the
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.16.0
0.17.0
5 changes: 3 additions & 2 deletions WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
# Airy Bazel tools
git_repository(
name = "com_github_airyhq_bazel_tools",
commit = "777db5e4d099dc960291b1187b0d41e8e444ae77",
commit = "2577f95b79aeef4c70a6aad1805b19ec707dbfa2",
remote = "https://github.com/airyhq/bazel-tools.git",
shallow_since = "1617185101 +0200",
shallow_since = "1617890651 +0200",
)

load("@com_github_airyhq_bazel_tools//:repositories.bzl", "airy_bazel_tools_dependencies", "airy_jvm_deps")
Expand Down Expand Up @@ -42,6 +42,7 @@ maven_install(
"io.jsonwebtoken:jjwt-impl:0.10.5",
"io.jsonwebtoken:jjwt-jackson:0.10.5",
"io.lettuce:lettuce-core:5.3.3.RELEASE",
"io.micrometer:micrometer-registry-prometheus:1.6.5",
"io.zonky.test:embedded-database-spring-test:1.5.1",
"javax.activation:javax.activation-api:1.2.0",
"javax.validation:validation-api:2.0.1.Final",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
package co.airy.core.api.admin;

import co.airy.avro.communication.Channel;
import co.airy.avro.communication.Metadata;
import co.airy.avro.communication.ChannelConnectionState;
import co.airy.avro.communication.Metadata;
import co.airy.core.api.admin.payload.ChannelsResponsePayload;
import co.airy.model.channel.ChannelPayload;
import co.airy.model.channel.dto.ChannelContainer;
import co.airy.model.metadata.MetadataKeys;
import co.airy.model.metadata.dto.MetadataMap;
import co.airy.spring.web.payload.EmptyResponsePayload;
import co.airy.uuid.UUIDv5;
import lombok.Data;
import lombok.NoArgsConstructor;
Expand All @@ -20,8 +19,9 @@

import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.util.List;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;

import static co.airy.model.channel.ChannelPayload.fromChannelContainer;
Expand All @@ -37,9 +37,9 @@ public ChannelsController(Stores stores) {
}

@PostMapping("/channels.list")
ResponseEntity<ChannelsResponsePayload> listChannels(@RequestBody @Valid ListChannelRequestPayload requestPayload) {
ResponseEntity<ChannelsResponsePayload> listChannels(@RequestBody(required = false) @Valid ListChannelRequestPayload requestPayload) {
final List<ChannelContainer> channels = stores.getChannels();
final String sourceToFilter = requestPayload.getSource();
final String sourceToFilter = Optional.ofNullable(requestPayload).map(ListChannelRequestPayload::getSource).orElse(null);
return ResponseEntity.ok(new ChannelsResponsePayload(channels.stream()
.filter((container) -> sourceToFilter == null || sourceToFilter.equals(container.getChannel().getSource()))
.map(ChannelPayload::fromChannelContainer)
Expand All @@ -50,7 +50,7 @@ ResponseEntity<ChannelsResponsePayload> listChannels(@RequestBody @Valid ListCha
ResponseEntity<?> getChannel(@RequestBody @Valid GetChannelRequestPayload requestPayload) {
final ChannelContainer container = stores.getChannel(requestPayload.getChannelId().toString());
if (container == null) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(new EmptyResponsePayload());
return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
}

return ResponseEntity.ok(fromChannelContainer(container));
Expand All @@ -61,7 +61,7 @@ ResponseEntity<?> updateChannel(@RequestBody @Valid UpdateChannelRequestPayload
final String channelId = requestPayload.getChannelId().toString();
final ChannelContainer container = stores.getChannel(channelId);
if (container == null) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(new EmptyResponsePayload());
return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
}

final MetadataMap metadataMap = container.getMetadataMap();
Expand Down Expand Up @@ -127,7 +127,7 @@ ResponseEntity<?> disconnect(@RequestBody @Valid ChannelDisconnectRequestPayload

final Channel channel = container.getChannel();
if (channel.getConnectionState().equals(ChannelConnectionState.DISCONNECTED)) {
return ResponseEntity.accepted().body(new EmptyResponsePayload());
return ResponseEntity.noContent().build();
}

channel.setConnectionState(ChannelConnectionState.DISCONNECTED);
Expand All @@ -139,7 +139,7 @@ ResponseEntity<?> disconnect(@RequestBody @Valid ChannelDisconnectRequestPayload
return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).build();
}

return ResponseEntity.ok(new EmptyResponsePayload());
return ResponseEntity.noContent().build();
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@
import co.airy.avro.communication.Tag;
import co.airy.avro.communication.TagColor;
import co.airy.core.api.admin.payload.CreateTagRequestPayload;
import co.airy.core.api.admin.payload.CreateTagResponsePayload;
import co.airy.core.api.admin.payload.DeleteTagRequestPayload;
import co.airy.core.api.admin.payload.ListTagsResponsePayload;
import co.airy.core.api.admin.payload.TagResponsePayload;
import co.airy.core.api.admin.payload.UpdateTagRequestPayload;
import co.airy.spring.web.payload.EmptyResponsePayload;
import co.airy.spring.web.payload.RequestErrorResponsePayload;
import org.apache.kafka.streams.state.KeyValueIterator;
import org.apache.kafka.streams.state.ReadOnlyKeyValueStore;
Expand Down Expand Up @@ -64,7 +62,11 @@ ResponseEntity<?> createTag(@RequestBody @Valid CreateTagRequestPayload payload)
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage());
}

return ResponseEntity.status(201).body(CreateTagResponsePayload.builder().id(tag.getId()).build());
return ResponseEntity.status(201).body(TagResponsePayload.builder()
.id(tag.getId())
.name(tag.getName())
.color(tag.getColor().toString())
.build());
}

@PostMapping("/tags.list")
Expand All @@ -87,14 +89,14 @@ ResponseEntity<ListTagsResponsePayload> listTags() {
}

@PostMapping("/tags.delete")
ResponseEntity<EmptyResponsePayload> deleteTag(@RequestBody @Valid DeleteTagRequestPayload payload) {
ResponseEntity<Void> deleteTag(@RequestBody @Valid DeleteTagRequestPayload payload) {
final Tag tag = stores.getTagsStore().get(payload.getId().toString());
if (tag == null) {
return ResponseEntity.notFound().build();
}

stores.deleteTag(tag);
return ResponseEntity.ok(new EmptyResponsePayload());
return ResponseEntity.noContent().build();
}

@PostMapping("/tags.update")
Expand All @@ -120,6 +122,6 @@ ResponseEntity<?> updateTag(@RequestBody @Valid UpdateTagRequestPayload payload)
} catch (ExecutionException | InterruptedException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage());
}
return ResponseEntity.ok(new EmptyResponsePayload());
return ResponseEntity.noContent().build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import co.airy.core.api.admin.payload.TemplatesResponsePayload;
import co.airy.core.api.admin.payload.UpdateTemplateRequestPayload;
import co.airy.model.template.TemplatePayload;
import co.airy.spring.web.payload.EmptyResponsePayload;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.HttpStatus;
Expand Down Expand Up @@ -68,7 +67,7 @@ ResponseEntity<?> deleteTemplate(@RequestBody @Valid DeleteTemplateRequestPayloa
return ResponseEntity.notFound().build();
}
stores.deleteTemplate(template);
return ResponseEntity.ok(new EmptyResponsePayload());
return ResponseEntity.noContent().build();
}

@PostMapping("/templates.list")
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.validation.Valid;
import javax.validation.constraints.NotNull;

@Data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.util.UUID;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ void canManageTags() throws Exception {

webTestHelper.post("/tags.update",
"{\"id\": \"" + tagId + "\", \"name\": \"new-name\", \"color\": \"" + color + "\"}", "user-id")
.andExpect(status().isOk());
.andExpect(status().isNoContent());

webTestHelper.post("/tags.list", "{}", "user-id")
.andExpect(status().isOk())
Expand All @@ -108,7 +108,7 @@ void canManageTags() throws Exception {
.andExpect(jsonPath("$.data[0].name").value(is("new-name")))
.andExpect(jsonPath("$.data[0].color").value(is("RED")));

webTestHelper.post("/tags.delete", "{\"id\": \"" + tagId + "\"}", "user-id").andExpect(status().isOk());
webTestHelper.post("/tags.delete", "{\"id\": \"" + tagId + "\"}", "user-id").andExpect(status().isNoContent());

//TODO wait for tag deletion
TimeUnit.SECONDS.sleep(5);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ void canManageTemplates() throws Exception {
.andExpect(status().isOk())
.andExpect(jsonPath("$.name").value(is("new-template-name"))), "could not update template");

webTestHelper.post("/templates.delete", "{\"id\": \"" + templateId + "\"}", "user-id").andExpect(status().isOk());
webTestHelper.post("/templates.delete", "{\"id\": \"" + templateId + "\"}", "user-id").andExpect(status().isNoContent());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,6 @@

import static co.airy.test.Timing.retryOnException;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,8 @@
import co.airy.core.api.auth.services.Password;
import co.airy.spring.auth.IgnoreAuthPattern;
import co.airy.spring.jwt.Jwt;
import co.airy.spring.web.payload.EmptyResponsePayload;
import co.airy.spring.web.payload.RequestErrorResponsePayload;
import org.jdbi.v3.core.statement.UnableToExecuteStatementException;
import org.postgresql.util.PSQLException;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
Expand Down Expand Up @@ -115,7 +113,7 @@ ResponseEntity<?> requestPasswordReset(@RequestBody @Valid LoginRequestPayload l
// We execute async so that attackers cannot infer the presence of an email address
// based on response time.
executor.submit(() -> requestResetFor(email));
return ResponseEntity.ok(new EmptyResponsePayload());
return ResponseEntity.noContent().build();
}

@PostMapping("/users.password-reset")
Expand All @@ -134,7 +132,7 @@ ResponseEntity<?> passwordReset(@RequestBody @Valid PasswordResetRequestPayload

userDAO.changePassword(UUID.fromString(userId), passwordService.hashPassword(payload.getNewPassword()));

return ResponseEntity.ok(new EmptyResponsePayload());
return ResponseEntity.noContent().build();
}

private void requestResetFor(String email) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ void canResetPassword() throws Exception {
final String passwordResetRequest = "{\"email\":\"" + email + "\",\"password\":\"trustno1\"}";

webTestHelper.post("/users.request-password-reset", passwordResetRequest)
.andExpect(status().isOk());
.andExpect(status().isNoContent());

retryOnException(() -> Mockito.verify(mail).send(Mockito.eq(email), anyString(), anyString()),
"could not send email");
Expand All @@ -142,14 +142,14 @@ void canChangePassword() throws Exception {
doNothing().when(mail).send(Mockito.eq(email), anyString(), anyString());

webTestHelper.post("/users.request-password-reset", requestPasswordRequest)
.andExpect(status().isOk());
.andExpect(status().isNoContent());

Map<String, Object> refreshClaim = Map.of("reset_pwd_for", userId);
final String token = jwt.tokenFor(userId, refreshClaim);

final String passwordResetRequest = "{\"token\":\"" + token + "\", \"new_password\": \"super-safe-password\"}";

webTestHelper.post("/users.password-reset", passwordResetRequest).andExpect(status().isOk());
webTestHelper.post("/users.password-reset", passwordResetRequest).andExpect(status().isNoContent());
}

@Test
Expand Down
Loading

0 comments on commit 440d184

Please sign in to comment.