Skip to content

Commit

Permalink
March 2024 Release of the APL 2024.1 compliant APL Client Library
Browse files Browse the repository at this point in the history
For more details on this release refer to CHANGELOG.md

To learn about APL see: https://developer.amazon.com/docs/alexa-presentation-language/understand-apl.html
  • Loading branch information
amzn-abhinvs committed Mar 8, 2024
1 parent cb20599 commit 8985868
Show file tree
Hide file tree
Showing 18 changed files with 510 additions and 204 deletions.
4 changes: 4 additions & 0 deletions APLClient/include/APLClient/AplCoreConnectionManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,10 @@ class AplCoreConnectionManager
return m_Metrics;
}

const AplViewhostConfigPtr getViewhostConfig() const {
return m_viewhostConfig;
}

private:
/**
* Sends viewport scaling information to the client
Expand Down
43 changes: 43 additions & 0 deletions APLClient/include/APLClient/AplSession.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0/
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

#ifndef APL_CLIENT_LIBRARY_APL_SESSION_H_
#define APL_CLIENT_LIBRARY_APL_SESSION_H_

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wreorder"
#pragma push_macro("DEBUG")
#undef DEBUG
#include <apl/utils/session.h>
#pragma pop_macro("DEBUG")
#pragma GCC diagnostic pop

namespace APLClient {

class AplSession : public apl::Session {
public:
AplSession(bool isLogCommandEnabled);
void write(const char *filename, const char *func, const char *value) override;
void write(apl::LogCommandMessage&& message) override;

private:
const apl::SessionPtr m_defaultSession = apl::makeDefaultSession();
const bool m_isLogCommandEnabled;
std::string getLogInfoFrom(const apl::LogCommandMessage& message);
};

} // namespace APLClient

#endif // APL_CLIENT_LIBRARY_APL_SESSION_H_
3 changes: 3 additions & 0 deletions APLClient/include/APLClient/AplViewhostConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class AplViewhostConfig final {
AplViewhostConfig& disallowDialog(bool disallow);
AplViewhostConfig& scrollCommandDuration(unsigned int milliseconds);
AplViewhostConfig& animationQuality(const AnimationQuality& quality);
AplViewhostConfig& isLogCommandEnabled(bool isLogCommandEnabled);

unsigned int viewportWidth() const;
unsigned int viewportHeight() const;
Expand All @@ -68,6 +69,7 @@ class AplViewhostConfig final {
bool disallowDialog() const;
unsigned int scrollCommandDuration() const;
AnimationQuality animationQuality() const;
bool isLogCommandEnabled() const;

private:
unsigned int m_viewportWidth = 0;
Expand All @@ -85,6 +87,7 @@ class AplViewhostConfig final {
bool m_disallowDialog = false;
unsigned int m_scrollCommandDuration = 1000;
AnimationQuality m_animationQuality = AnimationQuality::NORMAL;
bool m_isLogCommandEnabled = false;
};

using AplViewhostConfigPtr = std::shared_ptr<AplViewhostConfig>;
Expand Down
4 changes: 2 additions & 2 deletions APLClient/src/AplCoreConnectionManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ double AplCoreConnectionManager::getOptionalValue(
double defaultValue) {
double value = defaultValue;
const auto& valueIt = jsonNode.FindMember(key);
if (valueIt != jsonNode.MemberEnd()) {
if (valueIt != jsonNode.MemberEnd() && valueIt->value.IsNumber()) {
value = valueIt->value.GetDouble();
}

Expand All @@ -263,7 +263,7 @@ std::string AplCoreConnectionManager::getOptionalValue(
const std::string& defaultValue) {
std::string value = defaultValue;
const auto& valueIt = jsonNode.FindMember(key);
if (valueIt != jsonNode.MemberEnd()) {
if (valueIt != jsonNode.MemberEnd() && valueIt->value.IsString()) {
value = valueIt->value.GetString();
}

Expand Down
5 changes: 4 additions & 1 deletion APLClient/src/AplCoreGuiRenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <rapidjson/document.h>

#include "APLClient/AplCoreGuiRenderer.h"
#include "APLClient/AplSession.h"

namespace APLClient {

Expand Down Expand Up @@ -71,7 +72,9 @@ void AplCoreGuiRenderer::renderDocument(
"APL-Web.Content.error");
tContentCreate->start();

auto content = apl::Content::create(std::move(document), apl::makeDefaultSession(),
const auto aplViewhostConfig = m_aplCoreConnectionManager->getViewhostConfig();
const bool isLogCommandEnabled = aplViewhostConfig ? aplViewhostConfig->isLogCommandEnabled() : false;
auto content = apl::Content::create(std::move(document), std::make_shared<AplSession>(isLogCommandEnabled),
m_aplCoreConnectionManager->getMetrics(), m_aplCoreConnectionManager->getRootConfig());

if (!content) {
Expand Down
69 changes: 69 additions & 0 deletions APLClient/src/AplSession.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0/
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

#include <rapidjson/document.h>
#include <rapidjson/writer.h>
#include <rapidjson/stringbuffer.h>

#include "APLClient/AplSession.h"

namespace APLClient {

AplSession::AplSession(bool isLogCommandEnabled)
: apl::Session(),
m_isLogCommandEnabled(isLogCommandEnabled) {
}

void AplSession::write(const char *filename, const char *func, const char *value) {
m_defaultSession->write(filename, func, value);
}

void AplSession::write(apl::LogCommandMessage&& message) {
if (m_isLogCommandEnabled) {
std::string info = getLogInfoFrom(message);

apl::LoggerFactory::instance().getLogger(message.level, "Log", "Command")
.session(*this)
.log("%s %s", message.text.c_str(), info.c_str());
}
}

std::string AplSession::getLogInfoFrom(const apl::LogCommandMessage& message) {
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
rapidjson::Document serializer;

writer.StartObject();

// Add arguments array if it's not empty
if (!message.arguments.empty()) {
writer.Key("arguments");
rapidjson::Value serializedArguments = message.arguments.serialize(serializer.GetAllocator());
serializedArguments.Accept(writer); // Serialize into the writer directly
}

// Add origin object if it's not empty
if (!message.origin.empty()) {
writer.Key("origin");
rapidjson::Value serializedOrigin = message.origin.serialize(serializer.GetAllocator());
serializedOrigin.Accept(writer);
}

writer.EndObject();

return buffer.GetString();
}

} // namespace APLClient
11 changes: 11 additions & 0 deletions APLClient/src/AplViewhostConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@ AplViewhostConfig::animationQuality(const AnimationQuality& quality) {
return *this;
}

AplViewhostConfig&
AplViewhostConfig::isLogCommandEnabled(bool isLogCommandEnabled) {
m_isLogCommandEnabled = isLogCommandEnabled;
return *this;
}

unsigned int
AplViewhostConfig::viewportWidth() const {
return m_viewportWidth;
Expand Down Expand Up @@ -171,4 +177,9 @@ AplViewhostConfig::animationQuality() const {
return m_animationQuality;
}

bool
AplViewhostConfig::isLogCommandEnabled() const {
return m_isLogCommandEnabled;
}

} // namespace APLClient
3 changes: 2 additions & 1 deletion APLClient/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ AplCoreMetrics.cpp
AplCoreTextMeasurement.cpp
AplCoreLocaleMethods.cpp
AplClientRenderer.cpp
AplViewhostConfig.cpp)
AplViewhostConfig.cpp
AplSession.cpp)

target_include_directories(APLClient PUBLIC "${APLClient_SOURCE_DIR}/include")

Expand Down
152 changes: 152 additions & 0 deletions APLClient/test/AplSessionTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0/
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

#include "APLClient/AplSession.h"

#include <gtest/gtest.h>
#include <fstream>

namespace APLClient {
namespace test {

using namespace ::testing;

static const std::string TEXT = "test text";
static const std::string ARGUMENTS = "test arguments";
static const std::string ORIGIN = "test origin";

class AplSessionTest : public ::testing::Test {
public:
void SetUp() override;
std::string CaptureLog(apl::LogCommandMessage&& message);
bool FindText(std::string text, std::string logContents);

protected:
std::shared_ptr<AplSession> m_aplSession;
};

void AplSessionTest::SetUp() {
m_aplSession = std::make_shared<AplSession>(true);
}

std::string AplSessionTest::CaptureLog(apl::LogCommandMessage&& message) {
// Redirect stdout to a file
std::string logFile = "CapturedLogCommand.txt";
freopen(logFile.c_str(),"w", stdout);

// Write the log message
m_aplSession->write(std::move(message));

// Redirect stdout back to terminal
freopen("/dev/tty","w", stdout);

// Read log file
std::ifstream in(logFile);
std::string logContents((std::istreambuf_iterator<char>(in)),
std::istreambuf_iterator<char>());

// Close and delete the temp log file
in.close();
std::remove(logFile.c_str());

return logContents;
}

bool AplSessionTest::FindText(std::string text, std::string logContents) {
return logContents.find(text) != std::string::npos;
}

TEST_F(AplSessionTest, WriteLogCommandMessage) {
apl::LogCommandMessage message {
TEXT,
apl::LogLevel::kInfo,
apl::Object(ARGUMENTS),
apl::Object(ORIGIN)
};

std::string logContents = CaptureLog(std::move(message));

EXPECT_TRUE(FindText(TEXT, logContents));
EXPECT_TRUE(FindText(ARGUMENTS, logContents));
EXPECT_TRUE(FindText(ARGUMENTS, logContents));
}

TEST_F(AplSessionTest, WriteLogCommandMessageWithoutArguments) {
apl::LogCommandMessage message {
TEXT,
apl::LogLevel::kInfo,
NULL,
apl::Object(ORIGIN)
};

std::string logContents = CaptureLog(std::move(message));

EXPECT_TRUE(FindText(TEXT, logContents));
EXPECT_FALSE(FindText(ARGUMENTS, logContents));
EXPECT_TRUE(FindText(ORIGIN, logContents));
}

TEST_F(AplSessionTest, WriteLogCommandMessageWithoutOrigin) {
apl::LogCommandMessage message {
TEXT,
apl::LogLevel::kInfo,
apl::Object(ARGUMENTS),
NULL
};

std::string logContents = CaptureLog(std::move(message));

EXPECT_TRUE(FindText(TEXT, logContents));
EXPECT_TRUE(FindText(ARGUMENTS, logContents));
EXPECT_FALSE(FindText(ORIGIN, logContents));
}

TEST_F(AplSessionTest, WriteLogCommandMessageWhenDisabled) {
m_aplSession = std::make_shared<AplSession>(false);

apl::LogCommandMessage message {
TEXT,
apl::LogLevel::kInfo,
apl::Object(ARGUMENTS),
apl::Object(ORIGIN)
};

std::string logContents = CaptureLog(std::move(message));

EXPECT_FALSE(FindText(TEXT, logContents));
EXPECT_FALSE(FindText(ARGUMENTS, logContents));
EXPECT_FALSE(FindText(ORIGIN, logContents));
}

TEST_F(AplSessionTest, WriteLogWithFilenameFuncAndValue) {
std::string logFile = "CapturedLogCommand.txt";
freopen(logFile.c_str(),"w", stdout);

m_aplSession->write("filename", "function", "test value");

freopen("/dev/tty","w", stdout);

std::ifstream in(logFile);
std::string logContents((std::istreambuf_iterator<char>(in)),
std::istreambuf_iterator<char>());

in.close();
std::remove(logFile.c_str());

EXPECT_TRUE(FindText("test value", logContents));
}

} // namespace test
} // namespace APLClient
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Changelog for apl-client-library

## [2024.1]
This release adds support for version 2024.1 of the APL specification. Please also see APL Core Library for changes: [apl-core-library CHANGELOG](https://github.com/alexa/apl-core-library/blob/master/CHANGELOG.md)

### Added
- Add Log Command

### Changed

- Bug fixes

## [2023.3]
This release adds support for version 2023.3 of the APL specification. Please also see APL Core Library for changes: [apl-core-library CHANGELOG](https://github.com/alexa/apl-core-library/blob/master/CHANGELOG.md)

Expand Down
Loading

0 comments on commit 8985868

Please sign in to comment.